首页 > 编程语言 > 详细

后缀数组(学习笔记)

时间:2019-02-10 21:06:28      阅读:180      评论:0      收藏:0      [点我收藏+]

后缀数组用来处理一类字符串问题

学习博客

https://www.cnblogs.com/victorique/p/8480093.html#autoid-1-2-1

 

例题1 :hihocoder #1403 后缀数组一 重复旋律

最长可重叠重复K次子串问题

  转化为求 height 数组中长度为 k-1 的子序列的最小值,单调队列维护即可

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<stack>
#include<queue>
using namespace std;
typedef long long ll;

const int maxn = 20020;
const int maxm = 110;

int n,m,t,ans;
int a[maxn];
int c[maxn],sa[maxn],rk[maxn],x[maxn],y[maxn],height[maxn];

int head,tail,q[maxn],p[maxn];

void get_sa(){
    for(int i=1;i<=n;i++) x[i]=a[i],++c[x[i]];
    for(int i=2;i<=m;i++) c[i]+=c[i-1];
    for(int i=1;i<=n;i++) sa[c[x[i]]--]=i;
    
    for(int k=1;k<=n;k<<=1){
        int num=0;
        for(int i=n-k+1;i<=n;i++) y[++num]=i;
        for(int i=1;i<=n;i++) if(sa[i]>k) y[++num]=sa[i]-k;
        for(int i=1;i<=m;i++) c[i]=0;
        for(int i=1;i<=n;i++) ++c[x[i]];
        for(int i=2;i<=m;i++) c[i]+=c[i-1];
        for(int i=n;i>=1;i--) sa[c[x[y[i]]]--]=y[i],y[i]=0;
        swap(x,y);
        x[sa[1]]=1;
        num=1;
        for(int i=2;i<=n;i++){
            x[sa[i]]=(y[sa[i]]==y[sa[i-1]] && y[sa[i]+k]==y[sa[i-1]+k])?num:++num;
        }
        if(num==n) break;
        m=num;
    }
}

void get_height(){
    int k=0;
    for(int i=1;i<=n;i++) rk[sa[i]]=i;
    for(int i=1;i<=n;i++){
        if(rk[i]==1) continue;
        if(k) k--;
        int j=sa[rk[i]-1];
        while(i+k<=n && j+k<=n && a[i+k]==a[j+k]) k++;
        height[rk[i]]=k;
    }
}

ll read(){ ll s=0,f=1; char ch=getchar(); while(ch<0 || ch>9){ if(ch==-) f=-1; ch=getchar(); } while(ch>=0 && ch<=9){ s=s*10+ch-0; ch=getchar(); } return s*f; }

int main(){
    n=read(),t=read(),m=100;
    for(int i=1;i<=n;i++) a[i]=read();
    get_sa();
    get_height();

//    for(int i=1;i<=n;i++) printf("%d ",height[i]);
    head=0,tail=1; t--;
    if(t==0){
        printf("%d\n",n);
        return 0;
    }
    for(int i=1;i<=n;i++){
        while(head<=tail && q[tail]>height[i]) --tail;
        q[++tail]=height[i];
        p[tail]=i;
        while(p[head]<i-t+1) head++;
        if(i>=t) ans=max(ans,q[head]);
    }
    
    printf("%d\n",ans);

    return 0;
} 

 

例题2 :hihocoder #1407 后缀数组二 重复旋律2

最长不可重叠重复子串问题

  height数组不好直接来判断是否有重合,转化为二分判断性问题,二分一个k,表示我们假设串中存在长度为k的不可重叠重复子串,

遍历 height数组,如果相邻的 height>=k,则将其分为一组,如果一组中 maxsa-minsa>=k 的话,则成立

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<stack>
#include<queue>
using namespace std;
typedef long long ll;

const int maxn = 101000;

int n,m;
int a[maxn],sa[maxn],rk[maxn],c[maxn],x[maxn],y[maxn],height[maxn];

void get_sa(){
    for(int i=1;i<=n;i++) x[i]=a[i],++c[x[i]];
    for(int i=2;i<=m;i++) c[i]+=c[i-1];
    for(int i=1;i<=n;i++) sa[c[x[i]]--]=i;
    
    for(int k=1;k<=n;k<<=1){
        int num=0;
        for(int i=n-k+1;i<=n;i++) y[++num]=i;
        for(int i=1;i<=n;i++) if(sa[i]>k) y[++num]=sa[i]-k;
        for(int i=1;i<=m;i++) c[i]=0;
        for(int i=1;i<=n;i++) ++c[x[i]];
        for(int i=2;i<=m;i++) c[i]+=c[i-1];
        for(int i=n;i>=1;i--) sa[c[x[y[i]]]--]=y[i],y[i]=0;
        swap(x,y);
        x[sa[1]]=1;
        num=1;
        for(int i=2;i<=n;i++){
            x[sa[i]]=(y[sa[i]]==y[sa[i-1]] && y[sa[i]+k]==y[sa[i-1]+k])?num:++num; 
        }
        if(num==n) break;
        m=num;
    }
}

void get_height(){
    int k=0;
    for(int i=1;i<=n;i++) rk[sa[i]]=i;
    for(int i=1;i<=n;i++){
        if(rk[i]==1) continue;
        if(k) k--;
        int j=sa[rk[i]-1];
        while(i+k<=n && j+k<=n && a[i+k]==a[j+k]) k++;
        height[rk[i]]=k;
    }
}

bool check(int k){
    int minsa,maxsa;
    for(int i=1;i<=n;i++){
        if(height[i]<k){
            minsa=sa[i];
            maxsa=sa[i];
        }else{
            minsa=min(minsa,sa[i]);
            maxsa=max(maxsa,sa[i]);
        }
        if(maxsa-minsa>=k) return true;
    }
    return false;
}

ll read(){ ll s=0,f=1; char ch=getchar(); while(ch<0 || ch>9){ if(ch==-) f=-1; ch=getchar(); } while(ch>=0 && ch<=9){ s=s*10+ch-0; ch=getchar(); } return s*f; }

int main(){
    n=read(),m=1000;
    for(int i=1;i<=n;i++) a[i]=read();
    get_sa();
    get_height();
//    for(int i=1;i<=n;i++) printf("%d ",height[i]);

    int L=0,R=n,ans;
    while(L<=R){
        int mid=(L+R)/2;
        if(check(mid)){
            ans=mid;
            L=mid+1;
        }else{
            R=mid-1;
        }
    }
    
    printf("%d\n",ans);

    return 0;
}

 

后缀数组(学习笔记)

原文:https://www.cnblogs.com/tuchen/p/10360100.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!