【题目大意】
求两个字符串的最长公共子串。
【思路】
对第一个字符串建立后缀自动机,第二个字符串去匹配。cnt记录当前最长公共子串的长度,而ret记录答案。
p代表位置指针,初始在rt位置。
对于第二个字符串的某一位s[i],如果当前有s[i]孩子,则cnt+1,继续往后移动;否则沿着pre指针返回。如果pre指针返回到0,则将p回到rt,cnt清空为0;否则如果中间有点拥有s[i]孩子,cnt=step[]+1。
为什么cnt=step[]+1?不要忘了后缀自动机的本质是维护后缀,沿着pre指针跑就是往长度更小的后缀移动,某位置代表的后缀的最长长度为step[],再加上s[i],即是step[]+1。
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #include<cmath> 6 using namespace std; 7 const int MAXN=100000+5; 8 int n; 9 char str[2][MAXN]; 10 struct SAM 11 { 12 int step[MAXN*2],pre[MAXN*2],next[MAXN*2][26]; 13 int tot,last; 14 inline int newNode(int cnt) 15 { 16 step[++tot]=cnt; 17 pre[tot]=0; 18 for (int i=0;i<26;i++) next[tot][i]=0; 19 return tot; 20 } 21 22 inline void extend(int x) 23 { 24 int p=last; 25 int np=newNode(step[p]+1); 26 while (p && !next[p][x]) next[p][x]=np,p=pre[p]; 27 if (!p) pre[np]=1; 28 else 29 { 30 int q=next[p][x]; 31 if (step[q]==step[p]+1) pre[np]=q; 32 else 33 { 34 int nq=newNode(step[p]+1); 35 for (int i=0;i<26;i++) next[nq][i]=next[q][i]; 36 pre[nq]=pre[q]; 37 pre[q]=pre[np]=nq; 38 while (p&&next[p][x]==q) next[p][x]=nq,p=pre[p]; 39 } 40 41 } 42 last=np; 43 } 44 45 inline void clear() 46 { 47 int tot=0; 48 last=newNode(tot); 49 } 50 51 inline int Query() 52 { 53 int ret=0,cnt=0; 54 int p=1; 55 for(int i=0;str[1][i];i++) 56 { 57 int index=str[1][i]-‘a‘; 58 if(next[p][index]) p=next[p][index],cnt++; 59 else 60 { 61 while (p && !next[p][index]) p=pre[p]; 62 if(!p) p=1,cnt=0; 63 else cnt=step[p]+1,p=next[p][index]; 64 /*由于沿着pre返回得到的字符串是当前字符串的后缀,所以第一个拥有index孩子的就是最长满足的后缀,长度即为step+1*/ 65 } 66 ret=max(ret,cnt); 67 } 68 return ret; 69 } 70 }suf; 71 72 void init() 73 { 74 scanf("%d",&n); 75 scanf("%s",str[0]); 76 int len=strlen(str[0]); 77 suf.clear(); 78 for (int i=0;i<len;i++) suf.extend(str[0][i]-‘a‘); 79 scanf("%s",str[1]); 80 } 81 82 int main() 83 { 84 init(); 85 printf("%d",suf.Query()); 86 return 0; 87 }
时间: 2024-10-05 10:15:32