【传送门:SPOJ1811】
简要题意:
给出两个字符串,求出两个字符串的最长公共子串
题解:
后缀自动机
先用第一个字符串构建SAM,然后用第二个字符串去跑
假设当前的公共子串长度为s,在SAM的状态为p,匹配到第二个字符串的第i个字符
如果p存在连向第i个字符的边,则p=tr[p].son[st[i]-'a'+1],s++
否则让p去跳fail直到找到存在连向第i个字符的边为止
如果找不到,则p=root,s=0,不然p=tr[p].son[st[i]-'a'+1],s=tr[p].dep+1
参考代码:
#include<cstdio> #include<cstring> #include<cstdlib> #include<algorithm> #include<cmath> using namespace std; struct SAM { int son[27],fail,dep; }tr[510000];int root,cnt,last; int a[310000]; void add(int k) { int x=a[k]; int np=++cnt,p=last; tr[np].dep=k; while(p!=0&&tr[p].son[x]==0) tr[p].son[x]=np,p=tr[p].fail; if(p==0) tr[np].fail=root; else { int q=tr[p].son[x]; if(tr[q].dep==tr[p].dep+1) tr[np].fail=q; else { int nq=++cnt;tr[nq]=tr[q]; tr[nq].dep=tr[p].dep+1; tr[q].fail=tr[np].fail=nq; while(p!=0&&tr[p].son[x]==q) tr[p].son[x]=nq,p=tr[p].fail; } } last=np; } char st[310000]; int main() { scanf("%s",st+1); int len=strlen(st+1); cnt=root=last=1; for(int i=1;i<=len;i++) a[i]=st[i]-'a'+1,add(i); scanf("%s",st+1); len=strlen(st+1); for(int i=1;i<=len;i++) a[i]=st[i]-'a'+1; int p=root,s=0,ans=0; for(int i=1;i<=len;i++) { if(tr[p].son[a[i]]!=0) s++,p=tr[p].son[a[i]]; else { while(p!=0&&tr[p].son[a[i]]==0) p=tr[p].fail; if(p==0) p=root,s=0; else s=tr[p].dep+1,p=tr[p].son[a[i]]; } ans=max(ans,s); } printf("%d ",ans); return 0; }