zoukankan      html  css  js  c++  java
  • 后缀数组--经典题型

    后缀例题

    转自--后缀数组---处理字符串的有力工具 

    例1 :最长公共前缀

    问题描述:给定一个字符串,询问某两个后缀的最长公共前缀。

    算法分析:问题可以转化为:height数组的区间最小值。所以加上一个 RMQ+倍增算法模板。可以O(1)查询。

    题目+代码:略。


    例2:可重叠最长重复子串

    问题描述:给定一个字符串,求最长重复子串,这两个子串可以重叠。

    算法分析:就是在height数组中找一个最大值。为什么这样,略。

    题目+代码:略。


    例3:不可重叠最长重复子串

    问题描述:给定一个字符串,求最长重复子串,这两个子串不能重叠。

    算法分析:枚举长度,进行判断,枚举时长度可以二分。原问题转为:判断当长度为K问是否存在两个及以上的字符串并且没有重叠?

    解决这个问题,需要理解分组。将后缀排序,相邻两个的最长公共前缀如果大于等于K,那么一定存在长度为K的字符串是相同的。所以:排序后height数组大于等于K的连续个数+1 代表相同的字符串有多少个。

    最长的不重叠字符串,按heigt<k进行分组,分组之后每组中才会存在长度大于K的字符串。在每组中,判断有SA的最大最小是否大于等于K。为什么是这样,下面有解释:

    某两个最大不重叠长度=min(l1-l2,height),当我们按照height<K的分组那么每组中height都是大于等于K,所以每组中的SA极差就是最长的长度。

    题目+代码:https://vjudge.net/problem/POJ-1743 

     

      1 #include <cstdio>
      2 #include <cstring>
      3 #include <cctype>
      4 #include <cmath>
      5 #include <set>
      6 #include <map>
      7 #include <list>
      8 #include <queue>
      9 #include <deque>
     10 #include <stack>
     11 #include <string>
     12 #include <vector>
     13 #include <iostream>
     14 #include <algorithm>
     15 #include <stdlib.h>
     16 
     17 using namespace std;
     18 typedef long long LL;
     19 const int INF=2e9+1e8;
     20 const int MOD=1e9+7;
     21 const int MAXSIZE=1e6+5;
     22 const double eps=0.0000000001;
     23 void fre()
     24 {
     25     freopen("in.txt","r",stdin);
     26     freopen("out.txt","w",stdout);
     27 }
     28 #define memst(a,b) memset(a,b,sizeof(a))
     29 #define fr(i,a,n) for(int i=a;i<n;i++)
     30 
     31 int ranks[MAXSIZE],SA[MAXSIZE],height[MAXSIZE];
     32 int wa[MAXSIZE],wb[MAXSIZE],wvarr[MAXSIZE],wsarr[MAXSIZE];
     33 
     34 int cmp(int *r,int a,int b,int l)
     35 {
     36     return r[a]==r[b]&&r[a+l]==r[b+l];
     37 }
     38 void da(int *r,int *sa,int n,int m)
     39 {
     40     int i,j,p,*x=wa,*y=wb,*t;
     41     for(i=0; i<m; i++) wsarr[i]=0;
     42     for(i=0; i<n; i++) wsarr[x[i]=r[i]]++;
     43     for(i=1; i<m; i++) wsarr[i]+=wsarr[i-1];
     44     for(i=n-1; i>=0; i--) sa[--wsarr[x[i]]]=i;
     45     for(j=1, p=1; p<n; j<<=1,m=p)
     46     {
     47         for(p=0,i=n-j; i<n; i++) y[p++]=i;
     48         for(i=0; i<n; i++) if(sa[i]>=j) y[p++]=sa[i]-j;
     49         for(i=0; i<n; i++) wvarr[i]=x[y[i]];
     50         for(i=0; i<m; i++) wsarr[i]=0;
     51         for(i=0; i<n; i++) wsarr[wvarr[i]]++;
     52         for(i=1; i<m; i++) wsarr[i]+=wsarr[i-1];
     53         for(i=n-1; i>=0; i--) sa[--wsarr[wvarr[i]]]=y[i];
     54         for(t=x,x=y,y=t,p=1,x[sa[0]]=0,i=1; i<n; i++)
     55             x[sa[i]]=cmp(y,sa[i-1],sa[i],j)?p-1:p++;
     56     }
     57 }
     58 void calheight(int *r,int *sa,int n)
     59 {
     60     int i,j,k=0;
     61     for(i=1; i<=n; i++) ranks[sa[i]]=i;
     62     for(i=0; i<n; height[ranks[i++]]=k)
     63         for(k?k--:0,j=sa[ranks[i]-1]; r[i+k]==r[j+k]; k++);
     64     return;
     65 }
     66 
     67 bool Judge(int n,int k)
     68 {
     69     int mi,ma;
     70     mi=ma=SA[1];
     71     for(int i=2; i<=n; i++)
     72     {
     73         if(height[i]>=k)
     74         {
     75             mi=min(mi,SA[i]);
     76             ma=max(ma,SA[i]);
     77             continue;
     78         }
     79         if(ma-mi>=k) return true;
     80         mi=ma=SA[i];
     81     }
     82     return false;
     83 }
     84 
     85 
     86 int a[MAXSIZE];
     87 int main()
     88 {
     89     int n;
     90     while(scanf("%d",&n)&&n)
     91     {
     92         for(int i=0; i<n; i++) scanf("%d",&a[i]);
     93         for(int i=0; i<n-1; i++) a[i]=a[i+1]-a[i]+100;
     94         a[n-1]=0;
     95         da(a,SA,n,200);
     96         calheight(a,SA,n-1);
     97         int l,r,mid,ans=0;
     98         l=1,r=n-1;
     99         while(l<=r)
    100         {
    101             mid=(l+r)>>1;
    102             if(Judge(n,mid)) ans=mid,l=mid+1;
    103             else r=mid-1;
    104         }
    105         ans++;
    106         if(ans<5) printf("0
    ");
    107         else printf("%d
    ",ans);
    108     }
    109     return 0;
    110 }
    111 
    112 /**************************************************/
    113 /**             Copyright Notice                 **/
    114 /**  writer: wurong                              **/
    115 /**  school: nyist                               **/
    116 /**  blog  : http://blog.csdn.net/wr_technology  **/
    117 /**************************************************/
    View Code

    例4:可重复的K次最长重复子串

    问题描述:给定一个字符串,球出现至少K次的最长字符串长度,字符串可以重叠。

    算法分析:有了以上题目经验,这个题目就很简单了,排好序的height数组,分组之后连续大于某个数的个数+1就是重复子串个数,保证子串个数大于K就OK了。

    题目+代码:https://vjudge.net/problem/POJ-3261

     

      1 #include <cstdio>
      2 #include <cstring>
      3 #include <cctype>
      4 #include <cmath>
      5 #include <set>
      6 #include <map>
      7 #include <list>
      8 #include <queue>
      9 #include <deque>
     10 #include <stack>
     11 #include <string>
     12 #include <vector>
     13 #include <iostream>
     14 #include <algorithm>
     15 #include <stdlib.h>
     16 
     17 using namespace std;
     18 typedef long long LL;
     19 const int INF=2e9+1e8;
     20 const int MOD=1e9+7;
     21 const int MAXSIZE=1e6+5;
     22 const double eps=0.0000000001;
     23 void fre()
     24 {
     25     freopen("in.txt","r",stdin);
     26     freopen("out.txt","w",stdout);
     27 }
     28 #define memst(a,b) memset(a,b,sizeof(a))
     29 #define fr(i,a,n) for(int i=a;i<n;i++)
     30 
     31 int ranks[MAXSIZE],SA[MAXSIZE],height[MAXSIZE];
     32 int wa[MAXSIZE],wb[MAXSIZE],wvarr[MAXSIZE],wsarr[MAXSIZE];
     33 
     34 int cmp(int *r,int a,int b,int l)
     35 {
     36     return r[a]==r[b]&&r[a+l]==r[b+l];
     37 }
     38 void da(int *r,int *sa,int n,int m)
     39 {
     40     int i,j,p,*x=wa,*y=wb,*t;
     41     for(i=0; i<m; i++) wsarr[i]=0;
     42     for(i=0; i<n; i++) wsarr[x[i]=r[i]]++;
     43     for(i=1; i<m; i++) wsarr[i]+=wsarr[i-1];
     44     for(i=n-1; i>=0; i--) sa[--wsarr[x[i]]]=i;
     45     for(j=1, p=1; p<n; j<<=1,m=p)
     46     {
     47         for(p=0,i=n-j; i<n; i++) y[p++]=i;
     48         for(i=0; i<n; i++) if(sa[i]>=j) y[p++]=sa[i]-j;
     49         for(i=0; i<n; i++) wvarr[i]=x[y[i]];
     50         for(i=0; i<m; i++) wsarr[i]=0;
     51         for(i=0; i<n; i++) wsarr[wvarr[i]]++;
     52         for(i=1; i<m; i++) wsarr[i]+=wsarr[i-1];
     53         for(i=n-1; i>=0; i--) sa[--wsarr[wvarr[i]]]=y[i];
     54         for(t=x,x=y,y=t,p=1,x[sa[0]]=0,i=1; i<n; i++)
     55             x[sa[i]]=cmp(y,sa[i-1],sa[i],j)?p-1:p++;
     56     }
     57 }
     58 void calheight(int *r,int *sa,int n)
     59 {
     60     int i,j,k=0;
     61     for(i=1; i<=n; i++) ranks[sa[i]]=i;
     62     for(i=0; i<n; height[ranks[i++]]=k)
     63         for(k?k--:0,j=sa[ranks[i]-1]; r[i+k]==r[j+k]; k++);
     64     return;
     65 }
     66 
     67 bool judge(int n,int k,int val)
     68 {
     69     int cnt=1;
     70     for(int i=2; i<=n; i++)
     71     {
     72         if(height[i]>=val) cnt++;
     73         else cnt=1;
     74         if(cnt>=k) return 1;
     75     }
     76     return 0;
     77 }
     78 
     79 int a[MAXSIZE];
     80 int main(int argc,char *argv[])
     81 {
     82     int n,k;
     83     while(scanf("%d%d",&n,&k)!=EOF)
     84     {
     85         for(int i=0; i<n; i++)
     86             scanf("%d",&a[i]);
     87         a[n]=0;
     88         da(a,SA,n+1,20000+1);
     89         calheight(a,SA,n);
     90         int L=1,R=n;
     91         int mid,ans=0;
     92         while(L<=R)
     93         {
     94             mid=(L+R)>>1;
     95             if(judge(n,k,mid)) ans=mid,L=mid+1;
     96             else R=mid-1;
     97         }
     98         printf("%d
    ",ans);
     99     }
    100     return 0;
    101 }
    102 
    103 /**************************************************/
    104 /**             Copyright Notice                 **/
    105 /**  writer: wurong                              **/
    106 /**  school: nyist                               **/
    107 /**  blog  : http://blog.csdn.net/wr_technology  **/
    108 /**************************************************/
    View Code


    例5:不相同子串个数?

    题目描述:给定一个字符串,问其中有多少个不相同的子字符串?

    本题目传送门



    例6:最长回文子串。

    题目描述:给定一个字符串。求最长回文子串,一般求最长回文子串的长度。

    算法分析:法一:Manacher

    方法二: 穷举每一位, 然后计算以这个字符为中心的最长回文子串。 注意这里要分两种情况, 一是回文子串的长度为奇数, 二是长度为偶数。 两 种情况都可以转化 为 求一个后缀和一个反过来写的后缀的最长公共前缀。 具体的做法是: 将 整个字 符 串反过来写在原字符串后面, 中间用一个特殊的字符隔开。 这 样就把问题变为 了 求这个新的字符串的某两个后缀的最长公共前缀。

    题目+代码

    https://cn.vjudge.net/problem/HDU-3068

    后缀数组法:代码省略,就是求height数组的最大值。

    Manacher法:点击查看代码

    https://vjudge.net/problem/URAL-1297

    Manacher算法:因为是求最长回文子串中首先出现的在找最大p[]数组时记录第一个即可。代码省略。

    后缀数组法: 只需要记录height数组中最大的而下表最小的,即可。

     

      1 #include <cstdio>
      2 #include <cstring>
      3 #include <cctype>
      4 #include <cmath>
      5 #include <set>
      6 #include <map>
      7 #include <list>
      8 #include <queue>
      9 #include <deque>
     10 #include <stack>
     11 #include <string>
     12 #include <vector>
     13 #include <iostream>
     14 #include <algorithm>
     15 #include <stdlib.h>
     16 #include <time.h>
     17 
     18 using namespace std;
     19 typedef long long LL;
     20 const int INF=2e9+1e8;
     21 const int MOD=1e9+7;
     22 const int MAXSIZE=1e6+5;
     23 const double eps=0.0000000001;
     24 void fre()
     25 {
     26     freopen("in.txt","r",stdin);
     27     freopen("out.txt","w",stdout);
     28 }
     29 #define memst(a,b) memset(a,b,sizeof(a))
     30 #define fr(i,a,n) for(int i=a;i<n;i++)
     31 /**
     32 求第一个出现的最长回文子串。
     33 height数组最大的是最长回文子串的长度;
     34 
     35 */
     36 
     37 
     38 int Rank[MAXSIZE],SA[MAXSIZE],height[MAXSIZE];
     39 int wa[MAXSIZE],wb[MAXSIZE],wv[MAXSIZE],wss[MAXSIZE];
     40 int cmp(int *r,int a,int b,int l)
     41 {
     42     return r[a]==r[b]&&r[a+l]==r[b+l];
     43 }
     44 void da(int *r,int *sa,int n,int m)
     45 {
     46     int i,j,p,*x=wa,*y=wb,*t;
     47     for(i=0; i<m; i++) wss[i]=0;
     48     for(i=0; i<n; i++) wss[x[i]=r[i]]++;
     49     for(i=1; i<m; i++) wss[i]+=wss[i-1];
     50     for(i=n-1; i>=0; i--) sa[--wss[x[i]]]=i;
     51     for(j=1,p=1; p<n; j<<=1,m=p)
     52     {
     53         for(p=0,i=n-j; i<n; i++) y[p++]=i;
     54         for(i=0; i<n; i++) if(sa[i]>=j) y[p++]=sa[i]-j;
     55         for(i=0; i<n; i++) wv[i]=x[y[i]];
     56         for(i=0; i<m; i++) wss[i]=0;
     57         for(i=0; i<n; i++) wss[wv[i]]++;
     58         for(i=1; i<m; i++) wss[i]+=wss[i-1];
     59         for(i=n-1; i>=0; i--) sa[--wss[wv[i]]]=y[i];
     60         for(t=x,x=y,y=t,p=1,x[sa[0]]=0,i=1; i<n; i++)
     61             x[sa[i]]=cmp(y,sa[i-1],sa[i],j)?p-1:p++;
     62     }
     63     return;
     64 }
     65 void calheight(int *r,int *sa,int n)
     66 {
     67     int i,j,k=0;
     68     for(i=1; i<=n; i++) Rank[sa[i]]=i;
     69     for(i=0; i<n; height[Rank[i++]]=k)
     70         for(k?k--:0,j=sa[Rank[i]-1]; r[i+k]==r[j+k]; k++);
     71     return;
     72 }
     73 
     74 char str[2000+10];
     75 int rs[MAXSIZE];
     76 int main()
     77 {
     78     while(scanf("%s",str)!=EOF)
     79     {
     80         int len=strlen(str);
     81         for(int i=0;i<len;i++) rs[i]=(int)str[i];
     82         rs[len]=1;
     83         int n=2*len+1;
     84         for(int i=len+1,j=len-1;j>=0;j--,i++) rs[i]=(int)str[j];
     85         rs[n]=0;
     86         da(rs,SA,n+1,128);
     87         calheight(rs,SA,n);
     88         int pos=0,maxlen=0;
     89         for(int i=2;i<=n;i++)
     90             if(height[i]>maxlen)
     91             {
     92                 pos=min(SA[i],SA[i-1]);
     93                 maxlen=height[i];
     94             }
     95             else if(height[i]==maxlen)
     96                 pos=min(pos,min(SA[i],SA[i-1]));
     97       //  printf("maxlen=%d  start=%d
    ",maxlen,pos);
     98         for(int i=pos;i<pos+maxlen;i++)
     99             printf("%c",str[i]);
    100         printf("
    ");
    101     }
    102     return 0;
    103 }
    104 
    105 
    106 /**************************************************/
    107 /**             Copyright Notice                 **/
    108 /**  writer: wurong                              **/
    109 /**  school: nyist                               **/
    110 /**  blog  : http://blog.csdn.net/wr_technology  **/
    111 /**************************************************/
    View Code

       


    例7:连续重复子串  

    https://cn.vjudge.net/problem/POJ-2406

    题目描述:给定一个字符串L,已知这个字符串是由某个字符串S重复R次而得到的,求R的最大值。

    算法分析:穷举法;具体看代码注释。

    代码:写太多的,然后就不太想要写了。借鉴别人代码!

    还有一种方法就是 KMP 算法:Next数组就是可以回滚到字符串的某一位,判断末尾的Next数组的值。详见这里

      1 //代码来源: http://blog.csdn.net/superxtong/article/details/52082133
      2 
      3 #include<cstdio>
      4 #include<cstdlib>
      5 #include<cstring>
      6 #include<cmath>
      7 #include<vector>
      8 #include<algorithm>
      9 #define F(x) ((x)/3+((x)%3==1?0:tb))
     10 #define G(x) ((x)<tb?(x)*3+1:((x)-tb)*3+2)
     11 using namespace std;
     12 const int maxn = int(3e6)+10;
     13 const int N = maxn;
     14 int wa[maxn],wb[maxn],wv[maxn],ws[maxn];
     15 int RANK[N],height[N],rm[N],sa[N];
     16 char a[N];
     17 int b[N];
     18 int c0(int *r,int a,int b)
     19 {
     20     return r[a]==r[b]&&r[a+1]==r[b+1]&&r[a+2]==r[b+2];
     21 }
     22 int c12(int k,int *r,int a,int b)
     23 {
     24     if(k==2) return r[a]<r[b]||r[a]==r[b]&&c12(1,r,a+1,b+1);
     25     else return r[a]<r[b]||r[a]==r[b]&&wv[a+1]<wv[b+1];
     26 }
     27 void sort(int *r,int *a,int *b,int n,int m)
     28 {
     29     int i;
     30     for(i=0;i<n;i++) wv[i]=r[a[i]];
     31     for(i=0;i<m;i++) ws[i]=0;
     32     for(i=0;i<n;i++) ws[wv[i]]++;
     33     for(i=1;i<m;i++) ws[i]+=ws[i-1];
     34     for(i=n-1;i>=0;i--) b[--ws[wv[i]]]=a[i];
     35     return;
     36 }
     37 void dc3(int *r,int *sa,int n,int m) //涵义与DA 相同
     38 {
     39     int i,j,*san=sa+n,ta=0,tb=(n+1)/3,tbc=0,p;
     40     int *rn=r+n;
     41     r[n]=r[n+1]=0;
     42     for(i=0;i<n;i++) if(i%3!=0) wa[tbc++]=i;
     43     sort(r+2,wa,wb,tbc,m);
     44     sort(r+1,wb,wa,tbc,m);
     45     sort(r,wa,wb,tbc,m);
     46     for(p=1,rn[F(wb[0])]=0,i=1;i<tbc;i++)
     47         rn[F(wb[i])]=c0(r,wb[i-1],wb[i])?p-1:p++;
     48     if(p<tbc) dc3(rn,san,tbc,p);
     49     else for(i=0;i<tbc;i++) san[rn[i]]=i;
     50     for(i=0;i<tbc;i++) if(san[i]<tb) wb[ta++]=san[i]*3;
     51     if(n%3==1) wb[ta++]=n-1;
     52     sort(r,wb,wa,ta,m);
     53     for(i=0;i<tbc;i++) wv[wb[i]=G(san[i])]=i;
     54     for(i=0,j=0,p=0;i<ta && j<tbc;p++)
     55         sa[p]=c12(wb[j]%3,r,wa[i],wb[j])?wa[i++]:wb[j++];
     56     for(;i<ta;p++) sa[p]=wa[i++];
     57     for(;j<tbc;p++) sa[p]=wb[j++];
     58     return;
     59 }
     60 void calheight(int *r,int *sa,int n)
     61 {   // 此处N为实际长度
     62     int i,j,k=0;
     63     // height[]的合法范围为 1-N, 其中0是结尾加入的字符
     64     for(i=1;i<=n;i++)
     65         RANK[sa[i]]=i;
     66     // 根据SA求RANK
     67     for(i=0;i<n; height[RANK[i++]] = k )
     68         // 定义:h[i] = height[ RANK[i] ]
     69         for(k?k--:0,j=sa[RANK[i]-1];
     70             r[i+k]==r[j+k]; k++);
     71     //根据 h[i] >= h[i-1]-1 来优化计算height过程
     72 }
     73 void RMQ(int n)
     74 {
     75     int k = RANK[1];
     76     rm[k] = N;
     77     for(int i=k-1;i>=0;i--)
     78     {
     79         rm[i]=min(rm[i+1],height[i+1]);
     80     }
     81     for(int i=k+1;i<=n;i++)
     82     {
     83         rm[i]=min(rm[i-1],height[i]);
     84     }
     85 }
     86 void solve(int le)
     87 {
     88     for(int i=1;i<=le;i++)//枚举长度
     89     {
     90         if(le%i!=0)//不能整除的话,一定不能构成循环节。
     91             continue;
     92         if(RANK[i]+1!=RANK[0])
     93         //rank数组必须要相邻才能构成循环节。
     94         {
     95             continue;
     96         }
     97         if(height[RANK[0]]!=le-i)
     98         //若第一个和第二个的最长公共前缀不符合条件。
     99             continue;
    100         printf("%d
    ",le/i);
    101         return ;
    102     }
    103     printf("1
    ");
    104     return ;
    105 }
    106 long long nxt[N];
    107 int main (void)
    108 {
    109     while(~scanf("%s",a))
    110     {
    111         if(a[0]=='.')
    112             break;
    113         int le=strlen(a);
    114         for(int i=0;i<le;i++)
    115         {
    116             b[i]=a[i]-'a'+'0';
    117         }
    118         dc3(b,sa,le+1,200);
    119         calheight(b,sa,le);
    120         RMQ(le);
    121         solve(le);
    122     }
    123     return 0;
    124 }
    View Code
      


    例8:重复次数最多的连续重复子串。

    题目描述:给定一个字符串,求重复次数最多的子字符串。在给定字符串中找一个子串,它的重复次数最多的子字符串。

    算法分析:





    持续更新中

  • 相关阅读:
    【数学】Codeforces Round #470 (Div2) B
    【数学】At Coder 091 D题
    【2-SAT】The Ministers’ Major Mess UVALive – 4452
    【二分答案+2-SAT】Now or later UVALive
    【栈模拟dfs】Cells UVALive
    浅谈2-SAT(待续)
    【交叉染色法判断二分图】Claw Decomposition UVA
    【拓扑排序或差分约束】Guess UVALive
    【欧拉回路】UVA
    周总结8.15
  • 原文地址:https://www.cnblogs.com/coded-ream/p/7207931.html
Copyright © 2011-2022 走看看