后缀数组小结

让人想犯罪 __ 提交于 2020-03-04 07:05:54

后缀数组果然是个神奇的东西…看起来非常巧妙的样子。话说我花了很长时间终于把罗穗骞大神论文里的代码弄懂了。。。

论文在这里:

http://wenku.baidu.com/view/ed1be61e10a6f524ccbf85fd.html

(OrzOrzOrzOrz)

 

关于后缀数组的一些小应用:

POJ3693

给定一个字符串,求重复次数最多的连续重复子串。如果存在多个,则输出字典序最小的一个。

枚举长度 l,然后求长度为 l 的子串最多能连续出现几次。

http://www.cnblogs.com/wangziyun/archive/2013/03/18/2966628.html

 

POJ1743

给定一列数,求最长重复子串,且这两个子串不能重叠。

注意:这道题中的“重复”定义为整个子串加上或减去一个整数后和另一子串相同。

转化为差分序列后二分答案 k,然后把height数组分组,每组的后缀之间的height值不少于 k。判断每组中sa的最大值与最小值之差是否不小于 k。如果有一组满足,那么该答案就是满足条件的。这种分组的思想似乎十分常用。

View Code
 1 #include<cstdio>
 2 #include<cstdlib>
 3 #include<cstring>
 4 using namespace std;
 5 #define maxn 20010
 6 #define inf 0x3f3f3f3f
 7 
 8 int r[maxn],wa[maxn],wb[maxn],ws[maxn],wv[maxn],h[maxn],rank[maxn],sa[maxn],n,m;
 9 void calc(int n)
10 {
11     int i,j,k=0;
12     for (i=1;i<=n;i++) rank[sa[i]]=i;
13     for (i=1;i<=n;h[rank[i++]]=k)
14         for (k?k--:0,j=sa[rank[i]-1];r[i+k]==r[j+k];k++);
15 }
16 bool cmp(int *r,int i,int j,int l)
17 {
18     return r[i]==r[j] && r[i+l]==r[j+l];
19 }
20 void da(int n)
21 {
22     int i,j,p,*x=wa,*y=wb,*t;
23     for (i=0;i<=m;i++) ws[i]=0;
24     for (i=1;i<=n;i++) ws[x[i]=r[i]]++;
25     for (i=1;i<=m;i++) ws[i]+=ws[i-1];
26     for (i=n;i;i--) sa[ws[x[i]]--]=i;
27     for (j=p=1;p<=n;m=p-1,j<<=1)
28     {
29         for (p=1,i=n-j+1;i<=n;i++) y[p++]=i;
30         for (i=1;i<=n;i++) if (sa[i]>j) y[p++]=sa[i]-j;
31         for (i=0;i<=m;i++) ws[i]=0;
32         for (i=1;i<=n;i++) ws[wv[i]=x[y[i]]]++;
33         for (i=1;i<=m;i++) ws[i]+=ws[i-1];
34         for (i=n;i;i--) sa[ws[wv[i]]--]=y[i];
35         for (t=x,x=y,y=t,i=p=2,x[sa[1]]=1;i<=n;i++)
36             x[sa[i]]=cmp(y,sa[i-1],sa[i],j)?p-1:p++;
37     }
38     for (i=1;i<n;i++) sa[i]=sa[i+1];
39     calc(n-1);
40 }
41 bool check(int k)
42 {
43     int min=inf,max=0;
44     for (int i=1;i<=n;i++)
45     {
46         if (h[i]>=k)
47         {
48             if (sa[i]>max) max=sa[i];
49             if (sa[i]<min) min=sa[i];
50             if (sa[i-1]>max) max=sa[i-1];
51             if (sa[i-1]<min) min=sa[i-1];
52             if (max-min>=k) return 1;
53         }
54         else min=inf,max=0;
55     }
56     return 0;
57 }
58 
59 int main()
60 {
61     scanf("%d",&n);
62     while(n)
63     {
64         m=0;
65         for (int i=1;i<=n;i++) scanf("%d",&r[i]);
66         for (int i=1;i<n;i++) r[i]=r[i+1]-r[i];
67         for (int i=1;i<n;i++) if (r[i]<m) m=r[i];
68         for (int i=1;i<n;i++) r[i]-=(m-1);
69         for (int i=1;i<n;i++) if (r[i]>m) m=r[i];
70         r[n]=0;
71         da(n);
72         int l=1,r=n,mid;
73         while (l!=r-1)
74         {
75             mid=(l+r)>>1;
76             if (check(mid)) l=mid;
77             else r=mid;
78             if (r<4) break;
79         }
80         if (l<4) printf("0\n");
81         else printf("%d\n",l+1);
82         scanf("%d",&n);
83     }
84     return 0;
85 }

 

POJ3261

给定一列数,求最长的连续重复至少k次的子串,这k个子串可以重复。

同样二分答案 k,把height数组分组判定每一组中的后缀个数是否不少于 k。

View Code
 1 #include<cstdio>
 2 #include<cstring>
 3 #include<cstdlib>
 4 using namespace std;
 5 #define maxn 20010
 6 
 7 int wa[maxn],wb[maxn],ws[maxn],wv[maxn],r[maxn],n,m,h[maxn],sa[maxn],rank[maxn],k;
 8 
 9 bool cmp(int *r,int i,int j,int l)
10 {
11     return r[i]==r[j] && r[i+l]==r[j+l];
12 }
13 void calc(int n)
14 {
15     int i,j,k=0;
16     for (i=1;i<=n;i++) rank[sa[i]]=i;
17     for (i=1;i<=n;h[rank[i++]]=k)
18         for (k?k--:0,j=sa[rank[i]-1];r[i+k]==r[j+k];k++);
19 }
20 void da(int n,int m)
21 {
22     int *x=wa,*y=wb,*t,p,i,j;
23     for (i=0;i<=m;i++) ws[i]=0;
24     for (i=1;i<=n;i++) ws[x[i]=r[i]]++;
25     for (i=1;i<=m;i++) ws[i]+=ws[i-1];
26     for (i=n;i;i--) sa[ws[x[i]]--]=i;
27     for (j=1,p=1;p<n;m=p,j*=2)
28     {
29         for (p=1,i=n-j+1;i<=n;i++) y[p++]=i;
30         for (i=1;i<=n;i++) if (sa[i]>j) y[p++]=sa[i]-j;
31         for (i=0;i<=m;i++) ws[i]=0;
32         for (i=1;i<=n;i++) ws[wv[i]=x[y[i]]]++;
33         for (i=1;i<=m;i++) ws[i]+=ws[i-1];
34         for (i=n;i;i--) sa[ws[wv[i]]--]=y[i];
35         for (t=x,x=y,y=t,x[sa[1]]=1,i=2,p=1;i<=n;i++)
36             x[sa[i]]=cmp(y,sa[i-1],sa[i],j)?p:++p;
37     }
38     for (i=1;i<=n;i++) sa[i]=sa[i+1];
39     calc(n-1);
40 }
41 bool check(int x)
42 {
43     int cnt=0;
44     for (int i=1;i<=n;i++)
45     {
46         if (h[i]>=x)
47         {
48             cnt++;
49             if (cnt>=k-1) return 1;
50         }
51         else cnt=0;
52     }
53     return 0;
54 }
55 
56 int main()
57 {
58     scanf("%d%d",&n,&k);
59     for (int i=1;i<=n;i++) scanf("%d",&r[i]);
60     for (int i=1;i<=n;i++) if (r[i]>m) m=r[i];
61     r[n+1]=0;
62     da(n+1,m);
63     int l=1,r=n,mid;
64     while (l!=r-1)
65     {
66         mid=(l+r)>>1;
67         if (check(mid)) l=mid;
68         else r=mid;
69     }
70     printf("%d\n",l);
71     return 0;
72 }

 

POJ3415

给定两个字符串,求长度不小于 k 的公共子串的个数。

将两个字符串连起来,中间用一个特殊字符隔开,按height数组分组后,依次扫描,每扫到一个后缀,就统计这一组中所有与它不在同一子串中的后缀产生的长度不小于 k 的公共子串的个数。可以用一个单调栈来维护。具体的可以看这里:http://hi.baidu.com/wplzzz/item/224f91ee33c8a1355b2d64a5。这篇文章讲得非常详细。

View Code
  1 #include<cstdio>
  2 #include<cstring>
  3 #include<cstdlib>
  4 #include<algorithm>
  5 using namespace std;
  6 #define maxn 200010
  7 
  8 int w1[maxn],w2[maxn],ws[maxn],wv[maxn],h[maxn],sa[maxn],rank[maxn],r[maxn],n,m,l1,l2;
  9 bool cmp(int *r,int i,int j,int l)
 10 {
 11     return r[i]==r[j] && r[i+l]==r[j+l];
 12 }
 13 void calc(int n)
 14 {
 15     int i,j,k=0;
 16     for (i=1;i<=n;i++) rank[sa[i]]=i;
 17     for (i=1;i<=n;h[rank[i++]]=k)
 18         for (k?k--:0,j=sa[rank[i]-1];r[i+k]==r[j+k];k++);
 19 }
 20 void da(int n,int m)
 21 {
 22     int i,j,p,*x=w1,*y=w2,*t;
 23     for (i=0;i<=m;i++) ws[i]=0;
 24     for (i=1;i<=n;i++) ws[x[i]=r[i]]++;
 25     for (i=1;i<=m;i++) ws[i]+=ws[i-1];
 26     for (i=n;i;i--) sa[ws[x[i]]--]=i;
 27     for (j=1,p=1;p<=n;m=p,j<<=1)
 28     {
 29         for (p=1,i=n-j+1;i<=n;i++) y[p++]=i;
 30         for (i=1;i<=n;i++) if (sa[i]>j) y[p++]=sa[i]-j;
 31         for (i=0;i<=m;i++) ws[i]=0;
 32         for (i=1;i<=n;i++) ws[wv[i]=x[y[i]]]++;
 33         for (i=1;i<=m;i++) ws[i]+=ws[i-1];
 34         for (i=n;i;i--) sa[ws[wv[i]]--]=y[i];
 35         for (t=x,x=y,y=t,i=2,p=2,x[sa[1]]=1;i<=n;i++)
 36             x[sa[i]]=cmp(y,sa[i-1],sa[i],j)?p-1:p++;
 37     }
 38     for (i=1;i<n;i++) sa[i]=sa[i+1];
 39     calc(n-1);
 40 }
 41 char c[maxn],t[maxn>>1];
 42 struct stack
 43 {
 44     int v,wa,wb;
 45 }st[maxn];
 46 int top;
 47 
 48 long long solve(int len)
 49 {
 50     long long ans=0,sum1=0,sum0=0;
 51     for (int i=1;i<=len;i++) h[i]=max(0,h[i]-n+1);
 52     for (int i=1;i<=len;i++)
 53     {
 54             if (sa[i-1]<=l1) 
 55             {
 56                 st[top+1].wa=1;
 57                 st[top+1].wb=0;
 58                 sum0+=h[i];
 59             }
 60             else
 61             {
 62                 st[top+1].wb=1;
 63                 st[top+1].wa=0;
 64                 sum1+=h[i];
 65             }
 66             st[top+1].v=h[i];
 67             while (st[top+1].v<=st[top].v && top)
 68             {
 69                 sum0-=(st[top].v-st[top+1].v)*st[top].wa;
 70                 sum1-=(st[top].v-st[top+1].v)*st[top].wb;
 71                 st[top].v=st[top+1].v;
 72                 st[top].wa+=st[top+1].wa;
 73                 st[top].wb+=st[top+1].wb;
 74                 top--;
 75             }
 76             top++;
 77             if (sa[i]<=l1) ans+=sum1;
 78             else ans+=sum0;
 79     }
 80     return ans;
 81 }
 82 
 83 int main()
 84 {
 85     while (~scanf("%d",&n))
 86     {
 87         if (!n) break;
 88         memset(sa,0,sizeof(sa));
 89         memset(h,0,sizeof(h));
 90         memset(rank,0,sizeof(rank));
 91         int len=1;
 92         m=0;
 93         scanf("%s",c+1);
 94         scanf("%s",t+1);
 95         for (;c[len];len++) r[len]=c[len];
 96         l1=len-1;
 97         r[len++]=128;
 98         for (;t[len-l1-1];len++) r[len]=t[len-l1-1];
 99         l2=len-l1-2;
100         r[len]=0;
101         for (int i=1;i<=len;i++) m=(r[i]>m)?r[i]:m;
102         da(len,m);
103         len--;
104         printf("%lld\n",solve(len));
105     }
106     return 0;
107 }

 

我是弱菜,刷得太少了。。。

剩下的题请看 stick_jitb 大神的博客: http://www.cnblogs.com/stickjitb/archive/2013/03/23/2976530.html (Orz Orz Orz)

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!