POJ2774 后缀自动机&后缀数组

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了POJ2774 后缀自动机&后缀数组相关的知识,希望对你有一定的参考价值。

http://poj.org/problem?id=2774

题目大意就是给两个字符串,求最长公共子串。好像可以哈希切掉,但是为了练一练后缀数组以及学一学后缀自动机,我用不同方法终于A掉了这道题。

后缀数组:就是求出height数组然后扫一遍,求出满足条件的最大值(满足条件是指height所指的两个后缀要没有公共部分),是后缀数组的水题

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 using namespace std;
 5 char tmp[100005],s[200005];
 6 int n,sa[200005],rak[200005],height[200005],t[200005],t2[200005],c[300];
 7 void build_sa(int m)
 8 {
 9     int *x=t,*y=t2,p;
10     for(int i=0;i<m;i++)c[i]=0;
11     for(int i=0;i<n;i++)c[x[i]=s[i]]++;
12     for(int i=0;i<m;i++)c[i]+=c[i-1];
13     for(int i=n-1;i>=0;i--)sa[--c[x[i]]]=i;
14     for(int k=1;k<=n;k<<=1)
15     {
16         p=0;
17         for(int i=n-k;i<n;i++)y[p++]=i;
18         for(int i=0;i<n;i++)if(sa[i]>=k)y[p++]=sa[i]-k;
19         for(int i=0;i<m;i++)c[i]=0;
20         for(int i=0;i<n;i++)c[x[y[i]]]++;
21         for(int i=0;i<m;i++)c[i]+=c[i-1];
22         for(int i=n-1;i>=0;i--)sa[--c[x[y[i]]]]=y[i];
23         swap(x,y);
24         p=1;x[sa[0]]=0;
25         for(int i=1;i<n;i++)
26         x[sa[i]]=y[sa[i]]==y[sa[i-1]]&&y[sa[i]+k]==y[sa[i-1]+k]?p-1:p++;
27         if(p>=n)break;
28         m=p;
29     }
30 }
31 void printsa()
32 {
33     puts("");
34     for(int i=0;i<n;i++)
35     {
36         char *ss=s+sa[i];
37         while(*ss)putchar(*ss++);
38         puts("");
39     }
40 }
41 void get_height()
42 {
43     for(int i=0;i<n;i++)rak[sa[i]]=i;
44     int k=0;
45     for(int i=0;i<n;i++)
46     {
47         if(k)k--;
48         if(rak[i]==0){height[rak[i]]=k;continue;}
49         int j=sa[rak[i]-1];
50         while(s[i+k]==s[j+k])k++;
51         height[rak[i]]=k;
52     }
53 }
54 int main()
55 {
56     int mid;
57     while(scanf("%s",tmp)>0)
58     {
59         memset(s,0,sizeof(s));
60         mid=strlen(tmp);
61         tmp[mid]=$;
62         strcat(s,tmp);
63         scanf("%s",tmp);
64         tmp[strlen(tmp)+1]=\0;
65         tmp[strlen(tmp)]=#;
66         strcat(s,tmp);
67         n=strlen(s);
68         build_sa(130);
69         //printsa();
70         get_height();
71         int ans=0;
72         for(int i=1;i<n;i++)if(height[i]>ans&&((sa[i-1]<mid&&sa[i]>mid)||(sa[i-1]>mid&&sa[i]<mid)))ans=height[i];
73         printf("%d\n",ans);
74     }
75     return 0;
76 }

后缀自动机:基本上是一道模板题,用第一个字符串建好后缀自动机,再用第二个字符串匹配。。。感觉和AC自动机差不多(感觉数组写法比指针写法好看多了)

 1 #include<cstdio>
 2 #include<iostream>
 3 #include<algorithm>
 4 #include<cstring>
 5 using namespace std;
 6 const int N=101000;
 7 char s[N],t[N];
 8 int last,son[N<<1][30],dep[N<<1],pre[N<<1],lens,lent,tot,rt;
 9 void SAM(int alp)
10 {
11     int u=last;dep[++tot]=dep[last]+1;
12     last=tot;
13     while(u&&!son[u][alp])son[u][alp]=last,u=pre[u];
14     if(!u)pre[last]=rt;
15     else
16     {
17         int v=son[u][alp];
18         if(dep[v]==dep[u]+1)pre[last]=v;
19         else
20         {
21             dep[++tot]=dep[u]+1;
22             int nv=tot;
23             memcpy(son[nv],son[v],sizeof(son[nv]));
24             pre[nv]=pre[v];pre[v]=pre[last]=nv;
25             while(u&&son[u][alp]==v)son[u][alp]=nv,u=pre[u];
26         }
27     }
28 }
29 int solve()
30 {
31     int cur,tmp=0,ret=0;
32     cur=rt=last=++tot;
33     for(int i=1;i<=lens;i++)SAM(s[i]-a);
34     for(int i=1;i<=lent;i++)
35     {
36         if(son[cur][t[i]-a]){cur=son[cur][t[i]-a];tmp++;}
37         else
38         {
39             while(cur&&!son[cur][t[i]-a])cur=pre[cur];
40             if(!cur)cur=rt,tmp=0;
41             else tmp=dep[cur]+1,cur=son[cur][t[i]-a];
42         }
43         ret=max(ret,tmp);
44     }
45     return ret;
46 }
47 int main()
48 {
49     scanf("%s%s",s+1,t+1);
50     lens=strlen(s+1);lent=strlen(t+1);
51     printf("%d\n",solve());
52 }

 

以上是关于POJ2774 后缀自动机&后缀数组的主要内容,如果未能解决你的问题,请参考以下文章

HiHocoder1415 : 后缀数组三·重复旋律3 & Poj2774:Long Long Message

POJ 2774 Long Long Message(后缀数组)

poj2774 后缀数组

poj2774 后缀数组

POJ-2774 后缀数组基础题

POJ 2774 后缀数组 || 二分+哈希