SPOJ 1812 LCS2 后缀自动机
题意:
给10个字符串,求他们的最长公共子串。
题解:
只要明确后缀自动机中的每个节点都是其实都对应着逆序的后缀树的一个集合。
而nlcs(当前最长匹配长度)和lcs都是对于这个集合而言的,所有,每个点都要更新他的*f指针(逆序后缀树的父亲)所指的节点。
View Code
1 #include <iostream> 2 #include <cstring> 3 #include <cstdlib> 4 #include <algorithm> 5 #include <cstdio> 6 7 #define N 500050 8 9 using namespace std; 10 11 struct SAM 12 { 13 SAM *son[26],*f; 14 int l,nlcs,lcs;//当前lcs,之前所有串的lcs 15 }sam[N],*b[N],*head,*last; 16 17 int len,ans,cnt; 18 char str[N]; 19 int d[N]; 20 21 inline void add(int x) 22 { 23 SAM *p=&sam[++cnt],*jp=last; 24 p->l=p->lcs=last->l+1; last=p; 25 for(;jp&&!jp->son[x];jp=jp->f) jp->son[x]=p; 26 if(!jp) p->f=head; 27 else if(jp->l+1==jp->son[x]->l) p->f=jp->son[x]; 28 else 29 { 30 SAM *r=&sam[++cnt],*q=jp->son[x]; 31 *r=*q; r->l=r->lcs=jp->l+1; 32 p->f=q->f=r; 33 for(;jp&&jp->son[x]==q;jp=jp->f) jp->son[x]=r; 34 } 35 } 36 37 inline void read() 38 { 39 scanf("%s",str); 40 len=strlen(str); 41 head=last=&sam[cnt=0]; 42 for(int i=0;i<len;i++) add(str[i]-'a'); 43 } 44 45 inline void go() 46 { 47 for(int i=0;i<=cnt;i++) d[sam[i].l]++; 48 for(int i=1;i<=len;i++) d[i]+=d[i-1]; 49 for(int i=0;i<=cnt;i++) b[--d[sam[i].l]]=&sam[i]; 50 while(scanf("%s",str)!=EOF) 51 { 52 len=strlen(str); 53 for(int i=0;i<len;i++) str[i]-='a'; 54 int res=0; last=head; 55 for(int i=0;i<len;i++) 56 { 57 if(last->son[str[i]]) 58 { 59 res++; 60 last=last->son[str[i]]; 61 last->nlcs=max(res,last->nlcs); 62 } 63 else 64 { 65 for(;last&&!last->son[str[i]];last=last->f); 66 if(!last) res=0,last=head; 67 else 68 { 69 res=last->l+1; 70 last=last->son[str[i]]; 71 last->nlcs=max(res,last->nlcs); 72 } 73 } 74 } 75 for(int i=cnt;i>0;i--) 76 { 77 b[i]->lcs=min(b[i]->nlcs,b[i]->lcs); 78 b[i]->f->nlcs=max(b[i]->f->nlcs,b[i]->nlcs); 79 b[i]->nlcs=0; 80 } 81 } 82 ans=0; 83 for(int i=0;i<=cnt;i++) ans=max(ans,sam[i].lcs); 84 printf("%d\n",ans); 85 } 86 87 int main() 88 { 89 read(),go(); 90 return 0; 91 }
没有人能阻止我前进的步伐,除了我自己!