后缀数组--经典题型

后缀例题

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

例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\n");
107         else printf("%d\n",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\n",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\n",maxlen,pos);
 98         for(int i=pos;i<pos+maxlen;i++)
 99             printf("%c",str[i]);
100         printf("\n");
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\n",le/i);
101         return ;
102     }
103     printf("1\n");
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:重复次数最多的连续重复子串。

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

算法分析:

 

 





持续更新中

 

转载于:https://www.cnblogs.com/coded-ream/p/7207931.html

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值