【codevs3160】 LCS 【后缀自动机】
题意
给出两个字符串,求它们的最长公共子串。
分析
后缀自动机的基础应用。
比如两个字符串s1和s2,我们把s1建为SAM,然后根据s2跑,找出s2每个前缀的最长公共后缀。
我们可以理解为,当向尾部增加一个字母的时候,就按照匹配路径来走,当在SAM中找不到这样的字符串的时候,就要减少头部的字母,就顺着parent树往祖先结点走。
1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 #include <iostream> 5 #include <map> 6 7 using namespace std; 8 const int maxn=1e5+100; 9 char S[maxn],T[maxn]; 10 int n,m; 11 struct state{ 12 int len,link; 13 map<char,int>next; 14 }st[4*maxn]; 15 int last,cur,sz; 16 void init(){ 17 last=cur=0; 18 sz=1; 19 st[0].len=0; 20 st[0].link=-1; 21 // st[0].next.clear(); 22 } 23 void build_sam(char c){ 24 cur=sz++; 25 st[cur].len=st[last].len+1; 26 int p; 27 for(p=last;p!=-1&&!st[p].next.count(c);p=st[p].link){ 28 st[p].next[c]=cur; 29 } 30 if(p==-1) 31 st[cur].link=0; 32 else{ 33 int q=st[p].next[c]; 34 if(st[p].len+1==st[q].len){ 35 st[cur].link=q; 36 }else{ 37 int clone=sz++; 38 st[clone].len=st[p].len+1; 39 st[clone].next=st[q].next; 40 st[clone].link=st[q].link; 41 for(;p!=-1&&st[p].next[c]==q;p=st[p].link){ 42 st[p].next[c]=clone; 43 } 44 st[cur].link=st[q].link=clone; 45 } 46 } 47 last=cur; 48 } 49 50 int main(){ 51 scanf("%s%s",S,T); 52 n=strlen(S); 53 m=strlen(T); 54 init(); 55 for(int i=0;i<n;i++){ 56 build_sam(S[i]); 57 } 58 int cur=0; 59 int v=0,len=0,ans=0; 60 for(int i=0;i<m;i++){ 61 if(st[v].next.count(T[i])){ 62 v=st[v].next[T[i]]; 63 len++; 64 }else{ 65 while(v!=-1&&!st[v].next.count(T[i])){ 66 // printf("%d\n",v); 67 v=st[v].link; 68 len=st[v].len; 69 } 70 if(v==-1) 71 v=len=0; 72 else{ 73 v=st[v].next[T[i]]; 74 len++; 75 } 76 } 77 ans=max(ans,len); 78 } 79 printf("%d\n",ans); 80 81 82 return 0; 83 }