zoukankan      html  css  js  c++  java
  • 后缀数组专题

    后缀数组

    按照惯例,先上模板:

    /*
    *suffix array
    *倍增算法  O(n*logn)
    *待排序数组长度为n,放在0~n-1中,在最后面补一个0
    *build_sa( ,n+1, );//注意是n+1;
    *getHeight(,n);
    *例如:
    *n   = 8;
    *num[]   = { 1, 1, 2, 1, 1, 1, 1, 2, $ };注意num最后一位为0,其他大于0
    *rank[]  = { 4, 6, 8, 1, 2, 3, 5, 7, 0 };rank[0~n-1]为有效值,rank[n]必定为0无效值
    *sa[]    = { 8, 3, 4, 5, 0, 6, 1, 7, 2 };sa[1~n]为有效值,sa[0]必定为n是无效值
    *height[]= { 0, 0, 3, 2, 3, 1, 2, 0, 1 };height[2~n]为有效值
    *
    */
    
    int sa[MAXN];//SA数组,表示将S的n个后缀从小到大排序后把排好序的
                 //的后缀的开头位置顺次放入SA中
    int t1[MAXN],t2[MAXN],c[MAXN];//求SA数组需要的中间变量,不需要赋值
    int rank[MAXN],height[MAXN];
    //待排序的字符串放在s数组中,从s[0]到s[n-1],长度为n,且最大值小于m,
    //除s[n-1]外的所有s[i]都大于0,r[n-1]=0
    //函数结束以后结果放在sa数组中
    void build_sa(int s[],int n,int m)
    {
        int i,j,p,*x=t1,*y=t2;
        //第一轮基数排序,如果s的最大值很大,可改为快速排序
        for(i=0;i<m;i++)c[i]=0;
        for(i=0;i<n;i++)c[x[i]=s[i]]++;
        for(i=1;i<m;i++)c[i]+=c[i-1];
        for(i=n-1;i>=0;i--)sa[--c[x[i]]]=i;
        for(j=1;j<=n;j<<=1)
        {
            p=0;
            //直接利用sa数组排序第二关键字
            for(i=n-j;i<n;i++)y[p++]=i;//后面的j个数第二关键字为空的最小
            for(i=0;i<n;i++)if(sa[i]>=j)y[p++]=sa[i]-j;
            //这样数组y保存的就是按照第二关键字排序的结果
            //基数排序第一关键字
            for(i=0;i<m;i++)c[i]=0;
            for(i=0;i<n;i++)c[x[y[i]]]++;
            for(i=1;i<m;i++)c[i]+=c[i-1];
            for(i=n-1;i>=0;i--)sa[--c[x[y[i]]]]=y[i];
            //根据sa和x数组计算新的x数组
            swap(x,y);
            p=1;x[sa[0]]=0;
            for(i=1;i<n;i++)
                x[sa[i]]=y[sa[i-1]]==y[sa[i]] && y[sa[i-1]+j]==y[sa[i]+j]?p-1:p++;
            if(p>=n)break;
            m=p;//下次基数排序的最大值
        }
    }
    void getHeight(int s[],int n)
    {
        int i,j,k=0;
        for(i=0;i<=n;i++)rank[sa[i]]=i;
        for(i=0;i<n;i++)
        {
            if(k)k--;
            j=sa[rank[i]-1];
            while(s[i+k]==s[j+k])k++;
            height[rank[i]]=k;
        }
    }
    View Code

    参考资料:

    (某神论文): http://wenku.baidu.com/link?url=en6joFZZqv55q8AfAh8abnHbSyE6lu7C5maUFR5fTTOSUvus-tdZazcFOMxwv9nv-DvxxkGhFzhtSo4gen2l0ozeCTVywD52-1nOjCMjZdq

    height[i]=LCP(suf(sa[i]),suf(sa[i-1])).

     1,询问两个子串的最长公共前缀。

    由height数组的性质,LCP(suf(i),suf(j))=min(height[rank[i]]+1~height[rank[j]]).  转化为RMQ问题,o(nlogn)预处理,o(1)查询.

    2,求最长可重叠的重复子串。

    由于后缀数组按字典序构造,最长重复子串一定是名次相邻的,因此只需求height的最小值即可。

    3,求最长不可重叠的重复子串长度。

    二分查找长度,看长度是否符合条件,即不可重叠,sa[j]-sa[k]>len。

    bool check(int k)
    {
        int Max=sa[1],Min=sa[1];
        REP(i,2,n){
            if(height[i]<k) Max=Min=sa[i];
            else{
                Max=max(sa[i],Max);
                Min=min(sa[i],Min);
                if(Max-Min>k) return 1; ///注意这里的>号而不是>=
            }
        }
        return 0;
    }
    
            int l=1,r=n/2;
            while(l<=r){
                int m=(l+r)>>1;
                if(check(m)) ans=max(ans,m),l=m+1;
                else r=m-1;
            }
    View Code

    poj1743:

    求某一变化出现多次的子串长度。

    前后作差,转化为求最长不可重叠的重复子串长度。

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<cstdlib>
    #include<algorithm>
    #include<vector>
    #include<stack>
    #include<queue>
    #include<set>
    #include<map>
    #include<string>
    #include<math.h>
    #include<cctype>
    #define ll long long
    #define REP(i,a,b) for(int (i)=(a);(i)<=(b);(i)++)
    #define REPP(i,a,b,t) for(int (i)=(a);(i)<=(b);(i)+=(t))
    #define rep(i,a,b) for(int (i)=(a);(i)>=(b);(i)--)
    #define repp(i,a,b,t) for(int (i)=(a);(i)>=(b);(i)-=(t))
    #define PII pair<int,int>
    #define fst first
    #define snd second
    #define MP make_pair
    #define PB push_back
    #define RI(x) scanf("%d",&(x))
    #define RII(x,y) scanf("%d%d",&(x),&(y))
    #define RIII(x,y,z) scanf("%d%d%d",&(x),&(y),&(z))
    #define DRI(x) int (x);scanf("%d",&(x))
    #define DRII(x,y) int (x),(y);scanf("%d%d",&(x),&(y))
    #define DRIII(x,y,z) int (x),(y),(z);scanf("%d%d%d",&(x),&(y),&(z))
    #define RS(x) scanf("%s",x)
    #define RSS(x,y) scanf("%s%s",x,y)
    #define DRS(x) char x[maxn];scanf("%s",x)
    #define DRSS(x,y) char x[maxn],y[maxn];scanf("%s%s",x,y)
    #define MS0(a) memset((a),0,sizeof((a)))
    #define MS1(a) memset((a),-1,sizeof((a)))
    #define MS(a,b) memset((a),(b),sizeof((a)))
    #define ALL(v) v.begin(),v.end()
    #define SZ(v) (int)(v).size()
    
    using namespace std;
    
    const int maxn=1000100;
    const int INF=(1<<29);
    const double EPS=0.0000000001;
    const double Pi=acos(-1.0);
    
    int a[maxn],n;
    int str[maxn];
    int sa[maxn],height[maxn],rank[maxn];
    
    int t1[maxn],t2[maxn],c[maxn];
    void build_sa(int *s,int n,int m)
    {
        int i,j,p,*x=t1,*y=t2;
        REP(i,0,m-1) c[i]=0;
        REP(i,0,n-1) c[x[i]=s[i]]++;
        REP(i,1,m-1) c[i]+=c[i-1];
        rep(i,n-1,0) sa[--c[x[i]]]=i;
        for(j=1;j<=n;j<<=1){
            p=0;
            REP(i,n-j,n-1) y[p++]=i;
            REP(i,0,n-1) if(sa[i]>=j) y[p++]=sa[i]-j;
            REP(i,0,m-1) c[i]=0;
            REP(i,0,n-1) c[x[y[i]]]++;
            REP(i,1,m-1) c[i]+=c[i-1];
            rep(i,n-1,0) sa[--c[x[y[i]]]]=y[i];
            swap(x,y);
            p=1;x[sa[0]]=0;
            REP(i,1,n-1) x[sa[i]]=y[sa[i-1]]==y[sa[i]]&&y[sa[i-1]+j]==y[sa[i]+j]?p-1:p++;
            if(p>=n) break;
            m=p;
        }
    }
    
    void getHeight(int *s,int n)
    {
        int i,j,k=0;
        REP(i,0,n) rank[sa[i]]=i;
        REP(i,0,n-1){
            if(k) k--;
            j=sa[rank[i]-1];
            while(s[i+k]==s[j+k]) k++;
            height[rank[i]]=k;
        }
    }
    
    bool check(int k)
    {
        int Max=sa[1],Min=sa[1];
        REP(i,2,n){
            if(height[i]<k) Max=Min=sa[i];
            else{
                Max=max(sa[i],Max);
                Min=min(sa[i],Min);
                if(Max-Min>k) return 1;
            }
        }
        return 0;
    }
    
    int main()
    {
        freopen("in.txt","r",stdin);
        while(cin>>n,n){
            REP(i,1,n) RI(a[i]);
            REP(i,2,n) str[i-2]=a[i]-a[i-1]+100;
            n--;
            str[n]=0;
            build_sa(str,n+1,200);
            getHeight(str,n);
            int ans=0;
            int l=1,r=n/2;
            while(l<=r){
                int m=(l+r)>>1;
                if(check(m)) ans=max(ans,m),l=m+1;
                else r=m-1;
            }
            if(ans+1>=5) cout<<ans+1<<endl;
            else cout<<0<<endl;
        }
        return 0;
    }
    View Code

     poj3261:

    求至少出现k次的子串,可重叠。

    二分查找长度,看长度是否符合条件即出现k次或以上。对height分组,是否有一组区间长度>=k-1(k-1个height对应k个后缀)。

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<cstdlib>
    #include<algorithm>
    #include<vector>
    #include<stack>
    #include<queue>
    #include<set>
    #include<map>
    #include<string>
    #include<math.h>
    #include<cctype>
    #define ll long long
    #define REP(i,a,b) for(int (i)=(a);(i)<=(b);(i)++)
    #define REPP(i,a,b,t) for(int (i)=(a);(i)<=(b);(i)+=(t))
    #define rep(i,a,b) for(int (i)=(a);(i)>=(b);(i)--)
    #define repp(i,a,b,t) for(int (i)=(a);(i)>=(b);(i)-=(t))
    #define PII pair<int,int>
    #define fst first
    #define snd second
    #define MP make_pair
    #define PB push_back
    #define RI(x) scanf("%d",&(x))
    #define RII(x,y) scanf("%d%d",&(x),&(y))
    #define RIII(x,y,z) scanf("%d%d%d",&(x),&(y),&(z))
    #define DRI(x) int (x);scanf("%d",&(x))
    #define DRII(x,y) int (x),(y);scanf("%d%d",&(x),&(y))
    #define DRIII(x,y,z) int (x),(y),(z);scanf("%d%d%d",&(x),&(y),&(z))
    #define RS(x) scanf("%s",x)
    #define RSS(x,y) scanf("%s%s",x,y)
    #define DRS(x) char x[maxn];scanf("%s",x)
    #define DRSS(x,y) char x[maxn],y[maxn];scanf("%s%s",x,y)
    #define MS0(a) memset((a),0,sizeof((a)))
    #define MS1(a) memset((a),-1,sizeof((a)))
    #define MS(a,b) memset((a),(b),sizeof((a)))
    #define ALL(v) v.begin(),v.end()
    #define SZ(v) (int)(v).size()
    
    using namespace std;
    
    const int maxn=1000100;
    const int INF=(1<<29);
    const double EPS=0.0000000001;
    const double Pi=acos(-1.0);
    
    int n,k;
    int a[maxn];
    int sa[maxn],height[maxn],rank[maxn];
    int t1[maxn],t2[maxn],c[maxn];
    
    void build_sa(int *s,int n,int m)
    {
        int i,j,p,*x=t1,*y=t2;
        REP(i,0,m-1) c[i]=0;
        REP(i,0,n-1) c[x[i]=s[i]]++;
        REP(i,1,m-1) c[i]+=c[i-1];
        rep(i,n-1,0) sa[--c[x[i]]]=i;
        for(j=1;j<=n;j<<=1){
            p=0;
            REP(i,n-j,n-1) y[p++]=i;
            REP(i,0,n-1) if(sa[i]>=j) y[p++]=sa[i]-j;
            REP(i,0,m-1) c[i]=0;
            REP(i,0,n-1) c[x[y[i]]]++;
            REP(i,1,m-1) c[i]+=c[i-1];
            rep(i,n-1,0) sa[--c[x[y[i]]]]=y[i];
            swap(x,y);
            p=1;x[sa[0]]=0;
            REP(i,1,n-1) x[sa[i]]=y[sa[i-1]]==y[sa[i]]&&y[sa[i-1]+j]==y[sa[i]+j]?p-1:p++;
            if(p>=n) break;
            m=p;
        }
    }
    
    void getHeight(int *s,int n)
    {
        int i,j,k=0;
        REP(i,0,n) rank[sa[i]]=i;
        REP(i,0,n-1){
            if(k) k--;
            j=sa[rank[i]-1];
            while(s[i+k]==s[j+k]) k++;
            height[rank[i]]=k;
        }
    }
    
    bool check(int len)
    {
        int cnt=0;
        REP(i,2,n){
            if(height[i]>=len) cnt++;
            else cnt=0;
            if(cnt+1>=k) return 1;
        }
        return 0;
    }
    
    int main()
    {
        freopen("in.txt","r",stdin);
        while(cin>>n>>k){
            REP(i,0,n-1) RI(a[i]);
            a[n]=0;
            build_sa(a,n+1,1000010);
            getHeight(a,n);
            //REP(i,2,n) cout<<height[i]<<" ";cout<<endl;
            int ans=0;
            int l=1,r=n;
            while(l<=r){
                int m=(l+r)>>1;
                if(check(m)) ans=max(m,ans),l=m+1;
                else r=m-1;
            }
            cout<<ans<<endl;
        }
        return 0;
    }
    View Code

    SPOJ DISUBSTR && SPOJ SUBST1:

     求一个串中的所有不同的子串个数。

    子串就是后缀的前缀,height数组就是LCP,后面的就是不同的了,因此只要求所有的n-sa-height的和就行了。详情见上面某神论文。

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<cstdlib>
    #include<algorithm>
    #include<vector>
    #include<stack>
    #include<queue>
    #include<set>
    #include<map>
    #include<string>
    #include<math.h>
    #include<cctype>
    #define ll long long
    #define REP(i,a,b) for(int (i)=(a);(i)<=(b);(i)++)
    #define REPP(i,a,b,t) for(int (i)=(a);(i)<=(b);(i)+=(t))
    #define rep(i,a,b) for(int (i)=(a);(i)>=(b);(i)--)
    #define repp(i,a,b,t) for(int (i)=(a);(i)>=(b);(i)-=(t))
    #define PII pair<int,int>
    #define fst first
    #define snd second
    #define MP make_pair
    #define PB push_back
    #define RI(x) scanf("%d",&(x))
    #define RII(x,y) scanf("%d%d",&(x),&(y))
    #define RIII(x,y,z) scanf("%d%d%d",&(x),&(y),&(z))
    #define DRI(x) int (x);scanf("%d",&(x))
    #define DRII(x,y) int (x),(y);scanf("%d%d",&(x),&(y))
    #define DRIII(x,y,z) int (x),(y),(z);scanf("%d%d%d",&(x),&(y),&(z))
    #define RS(x) scanf("%s",x)
    #define RSS(x,y) scanf("%s%s",x,y)
    #define DRS(x) char x[maxn];scanf("%s",x)
    #define DRSS(x,y) char x[maxn],y[maxn];scanf("%s%s",x,y)
    #define MS0(a) memset((a),0,sizeof((a)))
    #define MS1(a) memset((a),-1,sizeof((a)))
    #define MS(a,b) memset((a),(b),sizeof((a)))
    #define ALL(v) v.begin(),v.end()
    #define SZ(v) (int)(v).size()
    
    using namespace std;
    
    const int maxn=1000100;
    const int INF=(1<<29);
    const double EPS=0.0000000001;
    const double Pi=acos(-1.0);
    
    char s[maxn];
    int a[maxn],n;
    int sa[maxn],height[maxn],rank[maxn];
    int t1[maxn],t2[maxn],c[maxn];
    
    void build_sa(int *s,int n,int m)
    {
        int i,j,p,*x=t1,*y=t2;
        REP(i,0,m-1) c[i]=0;
        REP(i,0,n-1) c[x[i]=s[i]]++;
        REP(i,1,m-1) c[i]+=c[i-1];
        rep(i,n-1,0) sa[--c[x[i]]]=i;
        for(j=1;j<=n;j<<=1){
            p=0;
            REP(i,n-j,n-1) y[p++]=i;
            REP(i,0,n-1) if(sa[i]>=j) y[p++]=sa[i]-j;
            REP(i,0,m-1) c[i]=0;
            REP(i,0,n-1) c[x[y[i]]]++;
            REP(i,1,m-1) c[i]+=c[i-1];
            rep(i,n-1,0) sa[--c[x[y[i]]]]=y[i];
            swap(x,y);
            p=1;x[sa[0]]=0;
            REP(i,1,n-1) x[sa[i]]=y[sa[i-1]]==y[sa[i]]&&y[sa[i-1]+j]==y[sa[i]+j]?p-1:p++;
            if(p>=n) break;
            m=p;
        }
    }
    
    void getHeight(int *s,int n)
    {
        int i,j,k=0;
        REP(i,0,n) rank[sa[i]]=i;
        REP(i,0,n-1){
            if(k) k--;
            j=sa[rank[i]-1];
            while(s[i+k]==s[j+k]) k++;
            height[rank[i]]=k;
        }
    }
    
    int main()
    {
        freopen("in.txt","r",stdin);
        DRI(T);
        while(T--){
            scanf("%s",s);
            n=strlen(s);
            REP(i,0,n) a[i]=s[i];
            build_sa(a,n+1,300);
            getHeight(a,n);
            ll ans=0;
            REP(i,1,n) ans+=n-sa[i]-height[i];
            cout<<ans<<endl;
        }
        return 0;
    }
    View Code

     poj2406:

    找字符串的循环节。

    kmp水过。。。

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<cstdlib>
    #include<algorithm>
    #include<vector>
    #include<stack>
    #include<queue>
    #include<set>
    #include<map>
    #include<string>
    #include<math.h>
    #include<cctype>
    #define ll long long
    #define REP(i,a,b) for(int (i)=(a);(i)<=(b);(i)++)
    #define REPP(i,a,b,t) for(int (i)=(a);(i)<=(b);(i)+=(t))
    #define rep(i,a,b) for(int (i)=(a);(i)>=(b);(i)--)
    #define repp(i,a,b,t) for(int (i)=(a);(i)>=(b);(i)-=(t))
    #define PII pair<int,int>
    #define fst first
    #define snd second
    #define MP make_pair
    #define PB push_back
    #define RI(x) scanf("%d",&(x))
    #define RII(x,y) scanf("%d%d",&(x),&(y))
    #define RIII(x,y,z) scanf("%d%d%d",&(x),&(y),&(z))
    #define DRI(x) int (x);scanf("%d",&(x))
    #define DRII(x,y) int (x),(y);scanf("%d%d",&(x),&(y))
    #define DRIII(x,y,z) int (x),(y),(z);scanf("%d%d%d",&(x),&(y),&(z))
    #define RS(x) scanf("%s",x)
    #define RSS(x,y) scanf("%s%s",x,y)
    #define DRS(x) char x[maxn];scanf("%s",x)
    #define DRSS(x,y) char x[maxn],y[maxn];scanf("%s%s",x,y)
    #define MS0(a) memset((a),0,sizeof((a)))
    #define MS1(a) memset((a),-1,sizeof((a)))
    #define MS(a,b) memset((a),(b),sizeof((a)))
    #define ALL(v) v.begin(),v.end()
    #define SZ(v) (int)(v).size()
    
    using namespace std;
    
    const int maxn=1000100;
    const int INF=(1<<29);
    const double EPS=0.0000000001;
    const double Pi=acos(-1.0);
    
    char s[maxn];
    int Next[maxn];
    
    void getNext(char *s,int n)
    {
        Next[0]=-1;
        int i=0,j=-1;
        while(i<n&&j<n){
            if(j==-1||s[i]==s[j]) Next[++i]=++j;
            else j=Next[j];
        }
    }
    
    int main()
    {
        freopen("in.txt","r",stdin);
        while(~RS(s)){
            if(strcmp(s,".")==0) break;
            int n=strlen(s);
            getNext(s,n);
            if(n%(n-Next[n])==0) cout<<n/(n-Next[n])<<endl;
            else puts("1");
        }
        return 0;
    }
    View Code

     后缀数组解法,很简单,枚举长度k,如果能构成循环,则LCP(suf(0),suf(k))==n-k。由于suf(0)是固定的,所以不需要用RMQ,预处理一下就行了。

    然而倍增的nlogn预处理被卡了。。。

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<cstdlib>
    #include<algorithm>
    #include<vector>
    #include<stack>
    #include<queue>
    #include<set>
    #include<map>
    #include<string>
    #include<math.h>
    #include<cctype>
    #define ll long long
    #define REP(i,a,b) for(int (i)=(a);(i)<=(b);(i)++)
    #define REPP(i,a,b,t) for(int (i)=(a);(i)<=(b);(i)+=(t))
    #define rep(i,a,b) for(int (i)=(a);(i)>=(b);(i)--)
    #define repp(i,a,b,t) for(int (i)=(a);(i)>=(b);(i)-=(t))
    #define PII pair<int,int>
    #define fst first
    #define snd second
    #define MP make_pair
    #define PB push_back
    #define RI(x) scanf("%d",&(x))
    #define RII(x,y) scanf("%d%d",&(x),&(y))
    #define RIII(x,y,z) scanf("%d%d%d",&(x),&(y),&(z))
    #define DRI(x) int (x);scanf("%d",&(x))
    #define DRII(x,y) int (x),(y);scanf("%d%d",&(x),&(y))
    #define DRIII(x,y,z) int (x),(y),(z);scanf("%d%d%d",&(x),&(y),&(z))
    #define RS(x) scanf("%s",x)
    #define RSS(x,y) scanf("%s%s",x,y)
    #define DRS(x) char x[maxn];scanf("%s",x)
    #define DRSS(x,y) char x[maxn],y[maxn];scanf("%s%s",x,y)
    #define MS0(a) memset((a),0,sizeof((a)))
    #define MS1(a) memset((a),-1,sizeof((a)))
    #define MS(a,b) memset((a),(b),sizeof((a)))
    #define ALL(v) v.begin(),v.end()
    #define SZ(v) (int)(v).size()
    
    using namespace std;
    
    const int maxn=1000100;
    const int INF=(1<<29);
    const double EPS=0.0000000001;
    const double Pi=acos(-1.0);
    
    char s[maxn];
    int str[maxn],n;
    int sa[maxn],height[maxn],rank[maxn];
    int t1[maxn],t2[maxn],c[maxn];
    int LCP[maxn];
    
    void build_sa(int *s,int n,int m)
    {
        int i,j,p,*x=t1,*y=t2;
        REP(i,0,m-1) c[i]=0;
        REP(i,0,n-1) c[x[i]=s[i]]++;
        REP(i,1,m-1) c[i]+=c[i-1];
        rep(i,n-1,0) sa[--c[x[i]]]=i;
        for(j=1;j<=n;j<<=1){
            p=0;  
            REP(i,n-j,n-1) y[p++]=i;
            REP(i,0,n-1) if(sa[i]>=j) y[p++]=sa[i]-j;
            REP(i,0,m-1) c[i]=0;
            REP(i,0,n-1) c[x[y[i]]]++;
            REP(i,1,m-1) c[i]+=c[i-1];
            rep(i,n-1,0) sa[--c[x[y[i]]]]=y[i];
            swap(x,y);
            p=1;x[sa[0]]=0;
            p=1;x[sa[0]]=0;
            REP(i,1,n-1)
                x[sa[i]]=y[sa[i-1]]==y[sa[i]]&&y[sa[i-1]+j]==y[sa[i]+j]?p-1:p++;
            if(p>=n) break;
            m=p;
        }
    }
    
    void getHeight(int *s,int n)
    {
        int i,j,k=0;
        REP(i,0,n) rank[sa[i]]=i;
        REP(i,0,n-1){
            if(k) k--;
            j=sa[rank[i]-1];
            while(s[i+k]==s[j+k]) k++;
            height[rank[i]]=k;
        }
    }
     
    int main()
    {
        freopen("in.txt","r",stdin);
        while(~RS(s)){
            if(strcmp(s,".")==0) return 0;
            n=strlen(s);
            REP(i,0,n) str[i]=s[i];
            build_sa(str,n+1,300);
            getHeight(str,n);
            int Min=height[rank[0]+1];
            REP(i,rank[0]+1,n){
                LCP[i]=Min=min(height[i],Min);
            }
            Min=height[rank[0]];
            rep(i,rank[0],2){
                LCP[i-1]=Min=min(height[i],Min);
            }
            int ans=1;
            REP(k,1,n/2){
                if(n%k==0){
                    if(LCP[rank[k]]==n-k){
                        ans=n/k;break;
                    }
                }
            }
            cout<<ans<<endl;
        }
    }
    View Code

     SPOJ REPEATS :

    求字符串中连续出现次数最多的串的次数。

    枚举长度,详解见上面的某神论文。

    后缀数组+RMQ查询。不会ST,怒建一颗线段树!!!1890ms线段树暴力水过。。

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<cstdlib>
    #include<algorithm>
    #include<vector>
    #include<stack>
    #include<queue>
    #include<set>
    #include<map>
    #include<string>
    #include<math.h>
    #include<cctype>
    #define ll long long
    #define REP(i,a,b) for(int (i)=(a);(i)<=(b);(i)++)
    #define REPP(i,a,b,t) for(int (i)=(a);(i)<=(b);(i)+=(t))
    #define rep(i,a,b) for(int (i)=(a);(i)>=(b);(i)--)
    #define repp(i,a,b,t) for(int (i)=(a);(i)>=(b);(i)-=(t))
    #define PII pair<int,int>
    #define fst first
    #define snd second
    #define MP make_pair
    #define PB push_back
    #define RI(x) scanf("%d",&(x))
    #define RII(x,y) scanf("%d%d",&(x),&(y))
    #define RIII(x,y,z) scanf("%d%d%d",&(x),&(y),&(z))
    #define DRI(x) int (x);scanf("%d",&(x))
    #define DRII(x,y) int (x),(y);scanf("%d%d",&(x),&(y))
    #define DRIII(x,y,z) int (x),(y),(z);scanf("%d%d%d",&(x),&(y),&(z))
    #define RS(x) scanf("%s",x)
    #define RSS(x,y) scanf("%s%s",x,y)
    #define DRS(x) char x[maxn];scanf("%s",x)
    #define DRSS(x,y) char x[maxn],y[maxn];scanf("%s%s",x,y)
    #define MS0(a) memset((a),0,sizeof((a)))
    #define MS1(a) memset((a),-1,sizeof((a)))
    #define MS(a,b) memset((a),(b),sizeof((a)))
    #define ALL(v) v.begin(),v.end()
    #define SZ(v) (int)(v).size()
    
    using namespace std;
    
    const int maxn=1000100;
    const int INF=(1<<29);
    const double EPS=0.0000000001;
    const double Pi=acos(-1.0);
    
    char s[maxn];
    int str[maxn],n;
    int sa[maxn],height[maxn],rank[maxn];
    int t1[maxn],t2[maxn],c[maxn];
    
    void build_sa(int *s,int n,int m)
    {
        int i,j,p,*x=t1,*y=t2;
        REP(i,0,m-1) c[i]=0;
        REP(i,0,n-1) c[x[i]=s[i]]++;
        REP(i,1,m-1) c[i]+=c[i-1];
        rep(i,n-1,0) sa[--c[x[i]]]=i;
        for(j=1;j<=n;j<<=1){
            p=0;
            REP(i,n-j,n-1) y[p++]=i;
            REP(i,0,n-1) if(sa[i]>=j) y[p++]=sa[i]-j;
            REP(i,0,m-1) c[i]=0;
            REP(i,0,n-1) c[x[y[i]]]++;
            REP(i,1,m-1) c[i]+=c[i-1];
            rep(i,n-1,0) sa[--c[x[y[i]]]]=y[i];
            swap(x,y);
            p=1;x[sa[0]]=0;
            REP(i,1,n-1) x[sa[i]]=y[sa[i-1]]==y[sa[i]]&&y[sa[i-1]+j]==y[sa[i]+j]?p-1:p++;
            if(p>=n) break;
            m=p;
        }
    }
    
    void getHeight(int *s,int n)
    {
        int i,j,k=0;
        REP(i,0,n) rank[sa[i]]=i;
        REP(i,0,n-1){
            if(k) k--;
            j=sa[rank[i]-1];
            while(s[i+k]==s[j+k]) k++;
            height[rank[i]]=k;
        }
    }
    
    int Min[maxn<<2];
    #define lson l,m,rt<<1
    #define rson m+1,r,rt<<1|1
    
    void push_up(int rt)
    {
        Min[rt]=min(Min[rt<<1],Min[rt<<1|1]);
    }
    
    void build(int l,int r,int rt)
    {
        if(l==r){
            Min[rt]=height[l];
            return;
        }
        int m=(l+r)>>1;
        build(lson);
        build(rson);
        push_up(rt);
    }
    
    int query(int L,int R,int l,int r,int rt)
    {
        if(L<=l&&r<=R){
            return Min[rt];
        }
        int m=(l+r)>>1;
        int res=INF;
        if(L<=m) res=min(res,query(L,R,lson));
        if(R>m) res=min(res,query(L,R,rson));
        return res;
    }
    
    int lcp(int a,int b)
    {
        a=rank[a];b=rank[b];
        //cout<<a<<" "<<b<<endl;
        if(a>b) swap(a,b);
        return query(a+1,b,1,n,1);
    }
    
    int main()
    {
       // freopen("in.txt","r",stdin);
        DRI(T);
        while(T--){
            RI(n);
            REP(i,0,n-1) cin>>s[i];
            REP(i,0,n-1) str[i]=s[i];
            str[n]=0;
            build_sa(str,n+1,300);
            getHeight(str,n);
            build(1,n,1);
            int ans=1;
            REP(L,1,n){
                for(int i=0;i+L<n;i+=L){
                    int K=0;
                    if(str[i]==str[i+L]){
                        K=lcp(i,i+L);
                    }
                    int cnt=K/L+1;
                    if(K%L){
                        int t=i-(L-K%L);
                        //cout<<"t="<<t<<endl;
                        if(t>=0&&lcp(t,t+L)>=K) cnt++;
                    }
                    //if(cnt>ans) cout<<"L="<<L<<endl;
                    ans=max(ans,cnt);
                }
            }
            cout<<ans<<endl;
        }
        return 0;
    }
    View Code

     抄了个ST的模板,330ms,果然快了很多

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<cstdlib>
    #include<algorithm>
    #include<vector>
    #include<stack>
    #include<queue>
    #include<set>
    #include<map>
    #include<string>
    #include<math.h>
    #include<cctype>
    #define ll long long
    #define REP(i,a,b) for(int (i)=(a);(i)<=(b);(i)++)
    #define REPP(i,a,b,t) for(int (i)=(a);(i)<=(b);(i)+=(t))
    #define rep(i,a,b) for(int (i)=(a);(i)>=(b);(i)--)
    #define repp(i,a,b,t) for(int (i)=(a);(i)>=(b);(i)-=(t))
    #define PII pair<int,int>
    #define fst first
    #define snd second
    #define MP make_pair
    #define PB push_back
    #define RI(x) scanf("%d",&(x))
    #define RII(x,y) scanf("%d%d",&(x),&(y))
    #define RIII(x,y,z) scanf("%d%d%d",&(x),&(y),&(z))
    #define DRI(x) int (x);scanf("%d",&(x))
    #define DRII(x,y) int (x),(y);scanf("%d%d",&(x),&(y))
    #define DRIII(x,y,z) int (x),(y),(z);scanf("%d%d%d",&(x),&(y),&(z))
    #define RS(x) scanf("%s",x)
    #define RSS(x,y) scanf("%s%s",x,y)
    #define DRS(x) char x[maxn];scanf("%s",x)
    #define DRSS(x,y) char x[maxn],y[maxn];scanf("%s%s",x,y)
    #define MS0(a) memset((a),0,sizeof((a)))
    #define MS1(a) memset((a),-1,sizeof((a)))
    #define MS(a,b) memset((a),(b),sizeof((a)))
    #define ALL(v) v.begin(),v.end()
    #define SZ(v) (int)(v).size()
    
    using namespace std;
    
    const int maxn=1000100;
    const int INF=(1<<29);
    const double EPS=0.0000000001;
    const double Pi=acos(-1.0);
    
    char s[maxn];
    int str[maxn],n;
    int sa[maxn],height[maxn],rank[maxn];
    int t1[maxn],t2[maxn],c[maxn];
    
    void build_sa(int *s,int n,int m)
    {
        int i,j,p,*x=t1,*y=t2;
        REP(i,0,m-1) c[i]=0;
        REP(i,0,n-1) c[x[i]=s[i]]++;
        REP(i,1,m-1) c[i]+=c[i-1];
        rep(i,n-1,0) sa[--c[x[i]]]=i;
        for(j=1;j<=n;j<<=1){
            p=0;
            REP(i,n-j,n-1) y[p++]=i;
            REP(i,0,n-1) if(sa[i]>=j) y[p++]=sa[i]-j;
            REP(i,0,m-1) c[i]=0;
            REP(i,0,n-1) c[x[y[i]]]++;
            REP(i,1,m-1) c[i]+=c[i-1];
            rep(i,n-1,0) sa[--c[x[y[i]]]]=y[i];
            swap(x,y);
            p=1;x[sa[0]]=0;
            REP(i,1,n-1) x[sa[i]]=y[sa[i-1]]==y[sa[i]]&&y[sa[i-1]+j]==y[sa[i]+j]?p-1:p++;
            if(p>=n) break;
            m=p;
        }
    }
    
    void getHeight(int *s,int n)
    {
        int i,j,k=0;
        REP(i,0,n) rank[sa[i]]=i;
        REP(i,0,n-1){
            if(k) k--;
            j=sa[rank[i]-1];
            while(s[i+k]==s[j+k]) k++;
            height[rank[i]]=k;
        }
    }
    
    int dp[maxn][20];
    int mm[maxn];
    
    void initRMQ(int n,int *b)
    {
        mm[0]=-1;
        REP(i,1,n){
            mm[i]=((i&(i-1))==0)?mm[i-1]+1:mm[i-1];
            dp[i][0]=b[i];
        }
        REP(j,1,mm[n]){
            for(int i=1;i+(1<<j)-1<=n;i++){
                dp[i][j]=min(dp[i][j-1],dp[i+(1<<(j-1))][j-1]);
            }
        }
    }
    
    int rmq(int x,int y)
    {
        int k=mm[y-x+1];
        return min(dp[x][k],dp[y-(1<<k)+1][k]);
    }
    
    int lcp(int a,int b)
    {
        a=rank[a];b=rank[b];
        if(a>b) swap(a,b);
        return rmq(a+1,b);
    }
    
    int main()
    {
       // freopen("in.txt","r",stdin);
        DRI(T);
        while(T--){
            RI(n);
            REP(i,0,n-1) cin>>s[i];
            REP(i,0,n-1) str[i]=s[i];
            str[n]=0;
            build_sa(str,n+1,300);
            getHeight(str,n);
            initRMQ(n,height);
            int ans=1;
            REP(L,1,n){
                for(int i=0;i+L<n;i+=L){
                    int K=0;
                    if(str[i]==str[i+L]){
                        K=lcp(i,i+L);
                    }
                    int cnt=K/L+1;
                    if(K%L){
                        int t=i-(L-K%L);
                        if(t>=0&&lcp(t,t+L)>=K) cnt++;
                    }
                    ans=max(ans,cnt);
                }
            }
            cout<<ans<<endl;
        }
        return 0;
    }
    View Code

     poj3693:

    求字符串中具有循环节且循环次数最多的子串,输出字典序最小的。

    找循环次数最多的方法和上面一样,关键是找字典序最小的,而字典序首先应该想到sa数组,因为sa数组就是后缀的字典序排名。方法是找到循环次数最大的所有可能的循环节长度,然后在sa数组中枚举lcp(sa[i],sa[i]+L)是否大于循环节*(循环次数-1),枚举到的第一个符合条件的一定是字典序最小的。

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<cstdlib>
    #include<algorithm>
    #include<set>
    #define REP(i,a,b) for(int i=a;i<=b;i++)
    #define rep(i,a,b) for(int i=a;i>=b;i--)
    #define MS0(a) memset(a,0,sizeof(a))
    
    using namespace std;
    
    const int maxn=1000100;
    
    char s[maxn];
    int str[maxn],n;
    int sa[maxn],height[maxn],rank[maxn];
    int t1[maxn],t2[maxn],c[maxn];
    
    bool cmp(int *r,int a,int b,int l)
    {
        return r[a]==r[b] && r[a+l]==r[b+l];
    }
    
    void build_sa(int *s,int n,int m)
    {
        int i,j,p,*x=t1,*y=t2;
        REP(i,0,m-1) c[i]=0;
        REP(i,0,n-1) c[x[i]=s[i]]++;
        REP(i,1,m-1) c[i]+=c[i-1];
        rep(i,n-1,0) sa[--c[x[i]]]=i;
        for(j=1;j<=n;j<<=1){
            p=0;
            REP(i,n-j,n-1) y[p++]=i;
            REP(i,0,n-1) if(sa[i]>=j) y[p++]=sa[i]-j;
            REP(i,0,m-1) c[i]=0;
            REP(i,0,n-1) c[x[y[i]]]++;
            REP(i,1,m-1) c[i]+=c[i-1];
            rep(i,n-1,0) sa[--c[x[y[i]]]]=y[i];
            swap(x,y);
            p=1;x[sa[0]]=0;
            REP(i,1,n-1)
                x[sa[i]]=cmp(y,sa[i-1],sa[i],j)?p-1:p++;
            if(p>=n) break;
            m=p;
        }
    }
    
    void getHeight(int *s,int n)
    {
        int i,j,k=0;
        REP(i,0,n) rank[sa[i]]=i;
        REP(i,0,n-1){
            if(k) k--;
            j=sa[rank[i]-1];
            while(s[i+k]==s[j+k]) k++;
            height[rank[i]]=k;
        }
    }
    
    int dp[maxn][20];
    int mm[maxn];
    void initRMQ(int n,int *b)
    {
        mm[0]=-1;
        REP(i,1,n){
            mm[i]=((i&(i-1))==0)?mm[i-1]+1:mm[i-1];
            dp[i][0]=b[i];
        }
        REP(j,1,mm[n]){
            for(int i=1;i+(1<<j)-1<=n;i++)
                dp[i][j]=min(dp[i][j-1],dp[i+(1<<(j-1))][j-1]);
        }
    }
    int rmq(int x,int y)
    {
        int k=mm[y-x+1];
        return min(dp[x][k],dp[y-(1<<k)+1][k]);
    }
    
    int lcp(int a,int b)
    {
        a=rank[a];b=rank[b];
        if(a>b) swap(a,b);
        return rmq(a+1,b);
    }
    
    int main()
    {
        freopen("in.txt","r",stdin);
        int casen=1;
        while(~scanf("%s",s)&&strcmp(s,"#")){
            n=strlen(s);
            REP(i,0,n) str[i]=s[i];
            build_sa(str,n+1,300);
            getHeight(str,n);
            initRMQ(n,height);
            int maxcnt=1;
            set<int> maxL;
            REP(L,1,n){
                for(int i=0;i+L<n;i+=L){
                    int cnt=0,K=0;
                    if(str[i]==str[i+L]){
                        K=lcp(i,i+L);
                        cnt=K/L+1;
                        if(K%L&&i-(L-K%L)>=0){
                            if(lcp(i-(L-K%L),i-(L-K%L)+L)>K) cnt++;
                        }
                        if(cnt>=maxcnt){
                            maxcnt=cnt;
                            maxL.insert(L);
                        }
                    }
                }
            }
            printf("Case %d: ",casen++);
            if(maxcnt==1){
                char ans='z';
                REP(i,0,n-1){
                    if(s[i]<ans) ans=s[i];
                }
                cout<<ans<<endl;
            }
            else{
                bool flag=0;
                string ans="";
                for(int i=1;i<=n;i++){
                    for(set<int>::iterator it=maxL.begin();it!=maxL.end();++it){
                        int L=*it;
                        if(lcp(sa[i],sa[i]+L)>=L*maxcnt-L){
                            REP(k,sa[i],sa[i]+L*maxcnt-1) ans+=s[k];
                            flag=1;break;
                        }
                    }
                    if(flag) break;
                }
                cout<<ans<<endl;
            }
        }
        return 0;
    }
    View Code

     poj2774:

    求两个串的最长连续公共子串。

    把两个串接在一起,中间加个'}',合成新串,再处理,本来加'#'的,居然WA了。。

    一开始的思路是对长度进行二分,复杂度nlogn,因为如果直接找height数组的话不一定有两个排名相邻的串是原来在两个串中的,然而完全没有必要,因为一定至少存在排名相邻的两个串在不同的原串中,因此只要扫一遍height数组就可以了,复杂度n。

    二分解法:

    #include<iostream>
    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<algorithm>
    #define REP(i,a,b) for(int i=a;i<=b;i++)
    #define rep(i,a,b) for(int i=a;i>=b;i--)
    #define MS0(a) memset(a,0,sizeof(a))
    
    using namespace std;
    
    const int maxn=1000100;
    
    char s[maxn],t[maxn];
    int str[maxn],n;
    int sa[maxn],height[maxn],rank[maxn];
    int t1[maxn],t2[maxn],c[maxn];
    int ls,lt;
    
    bool cmp(int *r,int a,int b,int l)
    {
        return r[a]==r[b]&&r[a+l]==r[b+l];
    }
    
    void build_sa(int *s,int n,int m)
    {
        int i,j,p,*x=t1,*y=t2;
        REP(i,0,m-1) c[i]=0;
        REP(i,0,n-1) c[x[i]=s[i]]++;
        REP(i,1,m-1) c[i]+=c[i-1];
        rep(i,n-1,0) sa[--c[x[i]]]=i;
        for(j=1;j<=n;j<<=1){
            p=0;
            REP(i,n-j,n-1) y[p++]=i;
            REP(i,0,n-1) if(sa[i]>=j) y[p++]=sa[i]-j;
            REP(i,0,m-1) c[i]=0;
            REP(i,0,n-1) c[x[y[i]]]++;
            REP(i,1,m-1) c[i]+=c[i-1];
            rep(i,n-1,0) sa[--c[x[y[i]]]]=y[i];
            swap(x,y);
            p=1;x[sa[0]]=0;
            REP(i,1,n-1) x[sa[i]]=cmp(y,sa[i-1],sa[i],j)?p-1:p++;
            if(p>=n) break;
            m=p;
        }
    }
    
    void getHeight(int *s,int n)
    {
        int i,j,k=0;
        REP(i,0,n) rank[sa[i]]=i;
        REP(i,0,n-1){
            if(k) k--;
            j=sa[rank[i]-1];
            while(s[i+k]==s[j+k]) k++;
            height[rank[i]]=k;
        }
    }
    
    bool check(int k)
    {
        int Max=sa[1],Min=sa[1];
        REP(i,2,n){
            if(height[i]>=k){
                Max=max(sa[i],Max);
                Min=min(sa[i],Min);
            }
            else{
                if(Max>ls&&Min<ls) return 1;
                Max=sa[i];
                Min=sa[i];
            }
        }
        return 0;
    }
    
    int main()
    {
        freopen("in.txt","r",stdin);
        while(~scanf("%s%s",s,t)){
            ls=strlen(s);
            lt=strlen(t);
            s[ls]='}';s[ls+1]='';
            strcat(s,t);
            n=strlen(s);
            REP(i,0,n) str[i]=s[i];
            build_sa(str,n+1,300);
            getHeight(str,n);
            int l=1,r=n/2,ans=0;
            while(l<r){
                int m=(l+r)>>1;
                if(check(m)) l=m+1,ans=max(m,ans);
                else r=m;
            }
            cout<<ans<<endl;
        }
        return 0;
    }
    View Code

    o(n)正解:、

    #include<iostream>
    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<algorithm>
    #define REP(i,a,b) for(int i=a;i<=b;i++)
    #define rep(i,a,b) for(int i=a;i>=b;i--)
    #define MS0(a) memset(a,0,sizeof(a))
    
    using namespace std;
    
    const int maxn=1000100;
    
    char s[maxn],t[maxn];
    int str[maxn],n;
    int sa[maxn],height[maxn],rank[maxn];
    int t1[maxn],t2[maxn],c[maxn];
    int ls,lt;
    
    bool cmp(int *r,int a,int b,int l)
    {
        return r[a]==r[b]&&r[a+l]==r[b+l];
    }
    
    void build_sa(int *s,int n,int m)
    {
        int i,j,p,*x=t1,*y=t2;
        REP(i,0,m-1) c[i]=0;
        REP(i,0,n-1) c[x[i]=s[i]]++;
        REP(i,1,m-1) c[i]+=c[i-1];
        rep(i,n-1,0) sa[--c[x[i]]]=i;
        for(j=1;j<=n;j<<=1){
            p=0;
            REP(i,n-j,n-1) y[p++]=i;
            REP(i,0,n-1) if(sa[i]>=j) y[p++]=sa[i]-j;
            REP(i,0,m-1) c[i]=0;
            REP(i,0,n-1) c[x[y[i]]]++;
            REP(i,1,m-1) c[i]+=c[i-1];
            rep(i,n-1,0) sa[--c[x[y[i]]]]=y[i];
            swap(x,y);
            p=1;x[sa[0]]=0;
            REP(i,1,n-1) x[sa[i]]=cmp(y,sa[i-1],sa[i],j)?p-1:p++;
            if(p>=n) break;
            m=p;
        }
    }
    
    void getHeight(int *s,int n)
    {
        int i,j,k=0;
        REP(i,0,n) rank[sa[i]]=i;
        REP(i,0,n-1){
            if(k) k--;
            j=sa[rank[i]-1];
            while(s[i+k]==s[j+k]) k++;
            height[rank[i]]=k;
        }
    }
    
    int main()
    {
        freopen("in.txt","r",stdin);
        while(~scanf("%s%s",s,t)){
            ls=strlen(s);
            lt=strlen(t);
            s[ls]='}';s[ls+1]='';
            strcat(s,t);
            n=strlen(s);
            REP(i,0,n) str[i]=s[i];
            build_sa(str,n+1,300);
            getHeight(str,n);
            int ans=0;
            REP(i,2,n){
                if(height[i]>ans){
                    int L=sa[i],R=sa[i-1];
                    if(L>R) swap(L,R);
                    if(L<ls&&R>ls){
                        ans=height[i];
                    }
                }
            }
            cout<<ans<<endl;
        }
        return 0;
    }
    View Code

     poj3294:

    给N个同样长度的字符串,求在N/2以上各字符串中出现的最长子串。若有多组解,按字典序输出。

    二分求出最大长度,判断长度是否合法。判断合法的方法是,给height按长度分组,同组内的次数大于N/2,说明长度合法。

    接着扫一遍height数组,输出答案,由于height数组是按sa数组来的,而sa数组是按字典序来的,正好符合题意的字典序。

    注意点,最后一组要特判,因为可能没有进入循环。

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<cstdlib>
    #include<algorithm>
    
    using namespace std;
    
    const int maxm=120;
    const int LEN=1200;
    const int maxn=1200100;
    
    char s[maxm][LEN];
    int N;
    int ls;
    char t[maxn];
    int str[maxn],n;
    bool vis[maxm];
    int sa[maxn],height[maxn],rank[maxn];
    int t1[maxn],t2[maxn],c[maxn];
    
    bool cmp(int *r,int a,int b,int l)
    {
        return r[a]==r[b]&&r[a+l]==r[b+l];
    }
    
    void build_sa(int *str,int n,int m)
    {
        int i,j,p,*x=t1,*y=t2;
        for(int i=0;i<m;i++) c[i]=0;
        for(int i=0;i<n;i++) c[x[i]=str[i]]++;
        for(int i=1;i<m;i++) c[i]+=c[i-1];
        for(int i=n-1;i>=0;i--) sa[--c[x[i]]]=i;
        for(int j=1;j<=n;j<<=1){
            p=0;
            for(int i=n-j;i<n;i++) y[p++]=i;
            for(int i=0;i<n;i++) if(sa[i]>=j) y[p++]=sa[i]-j;
            for(int i=0;i<m;i++) c[i]=0;
            for(int i=0;i<n;i++) c[x[y[i]]]++;
            for(int i=1;i<m;i++) c[i]+=c[i-1];
            for(int i=n-1;i>=0;i--) sa[--c[x[y[i]]]]=y[i];
            swap(x,y);
            p=1;x[sa[0]]=0;
            for(int i=1;i<n;i++) x[sa[i]]=cmp(y,sa[i-1],sa[i],j)?p-1:p++;
            if(p>=n) break;
            m=p;
        }
    }
    
    void getHeight(int *str,int n)
    {
        int i,j,k=0;
        for(int i=0;i<=n;i++) rank[sa[i]]=i;
        for(int i=0;i<n;i++){
            if(k) k--;
            j=sa[rank[i]-1];
            while(str[i+k]==str[j+k]) k++;
            height[rank[i]]=k;
        }
    }
    
    bool check(int m)
    {
        memset(vis,0,sizeof(vis));
        int cnt=0;
        for(int i=2;i<=n;i++){
            if(height[i]>=m){
                int x=sa[i-1]/(ls+1);
                int y=sa[i]/(ls+1);
                if(!vis[x]) cnt++,vis[x]=1;
                if(!vis[y]) cnt++,vis[y]=1;
            }
            else{
                if(cnt>N/2) return 1;
                cnt=0;
                memset(vis,0,sizeof(vis));
            }
        }
        return cnt>N/2;
    }
    
    int main()
    {
        freopen("in.txt","r",stdin);
        while(cin>>N&&N){
            for(int i=1;i<=N;i++) scanf("%s",s[i]);
            ls=strlen(s[1]);
            t[0]='';
            int ch=1;
            for(int i=1;i<=N;i++){
                strcat(t,s[i]);
                int lt=strlen(t);
                t[lt]=ch++;t[lt+1]='';
                if(ch==97) ch+=27;
            }
            n=strlen(t);
            for(int i=0;i<=n;i++) str[i]=t[i];
            build_sa(str,n+1,400);
            getHeight(str,n);
            int maxL=0;
            int l=1,r=ls;
            while(l<=r){
                int m=(l+r)>>1;
                //cout<<"l="<<l<<" r="<<r<<" m="<<m<<endl;
                if(check(m)) maxL=max(m,maxL),l=m+1;
                else r=m-1;
            }
            if(!maxL){
                puts("?
    ");
                continue;
            }
            memset(vis,0,sizeof(vis));
            int cnt=0;
            for(int i=2;i<=n;i++){
                if(height[i]>=maxL){
                    int x=sa[i-1]/(ls+1);
                    int y=sa[i]/(ls+1);
                    if(!vis[x]) cnt++,vis[x]=1;
                    if(!vis[y]) cnt++,vis[y]=1;
                }
                else{
                    if(cnt>N/2){
                        for(int j=sa[i-1];j<sa[i-1]+maxL;j++) putchar(t[j]);
                        puts("");
                    }
                    cnt=0;
                    memset(vis,0,sizeof(vis));
                }
            }
            if(cnt>N/2){
                for(int j=sa[n];j<sa[n]+maxL;j++) putchar(t[j]);
                puts("");
            }
            puts("");
        }
        return 0;
    }
    View Code

     SPOJ PHRASES

    给N个不同长度的字符串,求在每个字符串中都出现至少两次且不重叠的最长子串长度。

    二分求最大长度,输出即可。出现两次和不重叠就再开两个数组,一个记录次数,另一个记录出现的位置,位置满足同一字符串的出现的两次的子串的位置之差大于等于长度,即不重叠。

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<cstdlib>
    #include<algorithm>
    
    using namespace std;
    
    const int maxn=1000100;
    const int INF=(1<<29);
    const int maxm=20;
    const int LEN=11000;
    
    char s[maxm][LEN];
    int N;
    char t[maxn];
    int str[maxn],n;
    int sa[maxn],height[maxn],rank[maxn];
    int id[maxn];
    int vis[maxm];
    int t1[maxn],t2[maxn],c[maxn];
    int pos[maxm][3];
    
    bool cmp(int *r,int a,int b,int l)
    {
        return r[a]==r[b]&&r[a+l]==r[b+l];
    }
    
    void build_sa(int *str,int n,int m)
    {
        int i,j,p,*x=t1,*y=t2;
        for(int i=0;i<m;i++) c[i]=0;
        for(int i=0;i<n;i++) c[x[i]=str[i]]++;
        for(int i=1;i<m;i++) c[i]+=c[i-1];
        for(int i=n-1;i>=0;i--) sa[--c[x[i]]]=i;
        for(int j=1;j<=n;j<<=1){
            p=0;
            for(int i=n-j;i<n;i++) y[p++]=i;
            for(int i=0;i<n;i++) if(sa[i]>=j) y[p++]=sa[i]-j;
            for(int i=0;i<m;i++) c[i]=0;
            for(int i=0;i<n;i++) c[x[y[i]]]++;
            for(int i=0;i<m;i++) c[i]+=c[i-1];
            for(int i=n-1;i>=0;i--) sa[--c[x[y[i]]]]=y[i];
            swap(x,y);
            p=1;x[sa[0]]=0;
            for(int i=1;i<n;i++) x[sa[i]]=cmp(y,sa[i-1],sa[i],j)?p-1:p++;
            if(p>=n) break;
            m=p;
        }
    }
    
    void getHeight(int *str,int n)
    {
        int i,j,k=0;
        for(int i=0;i<=n;i++) rank[sa[i]]=i;
        for(int i=0;i<n;i++){
            if(k) k--;
            j=sa[rank[i]-1];
            while(str[i+k]==str[j+k]) k++;
            height[rank[i]]=k;
        }
    }
    
    bool check(int m)
    {
        int cnt=0;
        memset(vis,0,sizeof(vis));
        memset(pos,-1,sizeof(pos));
        for(int i=2;i<=n;i++){
            if(height[i]>=m){
                int x=id[sa[i-1]];
                int p=sa[i-1];
                if(vis[x]<2&&(pos[x][vis[x]]==-1||abs(p-pos[x][vis[x]])>=m))
                    vis[x]++,cnt++,pos[x][vis[x]]=p;
            }
            else{
                int x=id[sa[i-1]];
                int p=sa[i-1];
                if(vis[x]<2&&(pos[x][vis[x]]==-1||abs(p-pos[x][vis[x]])>=m))
                    vis[x]++,cnt++,pos[x][vis[x]]=p;
                if(cnt==2*N) return 1;
                cnt=0;
                memset(vis,0,sizeof(vis));
                memset(pos,-1,sizeof(pos));
            }
        }
        int x=id[sa[n]];
        int p=sa[n];
        if(vis[x]<2&&(pos[x][vis[x]]==-1||abs(p-pos[x][vis[x]])>=m))
            vis[x]++,cnt++,pos[x][vis[x]]=p;
        return cnt==2*N;
    }
    
    int main()
    {
       // freopen("in.txt","r",stdin);
        int T;
        cin>>T;
        while(T--){
            cin>>N;
            for(int i=1;i<=N;i++) scanf("%s",s[i]);
            t[0]='';
            memset(id,0,sizeof(id));
            int ch=1;
            for(int i=1;i<=N;i++){
                int st=strlen(t);
                strcat(t,s[i]);
                int lt=strlen(t);
                t[lt]=ch++;t[lt+1]='';
                if(ch==97) ch+=27;
                lt=strlen(t);
                for(int j=st;j<lt;j++) id[j]=i;
            }
            n=strlen(t);
            for(int i=0;i<=n;i++) str[i]=t[i];
            build_sa(str,n+1,400);
            getHeight(str,n);
            int ans=0;
            int l=0,r=n;
            while(l<r){
                int m=(l+r)>>1;
                if(check(m)) ans=max(ans,m),l=m+1;
                else r=m;
            }
            cout<<ans<<endl;
        }
        return 0;
    }
    View Code
    没有AC不了的题,只有不努力的ACMER!
  • 相关阅读:
    C# 创建线程
    离最近发表时间代码
    ORA12154: TNS: 无法解析指定的连接标识符
    SQL和LINQ按年月、按类型显示文章篇数
    对象的序列化和反序列化
    父类与子类间的隐藏与重写
    C# 进程启动与关闭
    猫叫老鼠跑的事件例子
    第十八章 12判断string类型字符串是否为空 简单
    第十八章 11 string字符串的比较 简单
  • 原文地址:https://www.cnblogs.com/--560/p/4727537.html
Copyright © 2011-2022 走看看