http://www.spoj.com/problems/LCS2/
题意:求多个串的最长公共子串。每个字符串不超过100000
思路:也是SAM的经典应用了,基本思路和LCS一样(如果不太清楚的可以先看看这里http://blog.csdn.net/dyx404514/article/details/8718963)
首先还是构造第一个字符串的SAM,我们在SAM的每个状态里多维护几个值:mi表示不同的字符串在该状态匹配长度的最小值(为什么是最小值这个留给大家思考),ma表示当前正在与A进行匹配的字符串在该状态匹配长度的最大值。ma可以按LCS里的思路求tmp的时候求出,mi要在当前字符串匹配完之后再求。
具体方法是:将SAM拓扑排序后自底向上更新,设当前状态为p,如果p->mi>p->ma,则p->mi=p->ma,如果p有父亲,设q为p的父亲,若p->ma>q->ma,则由SAM的性质可知,p的最大匹配数可以向上传递给它的父亲,这里要注意的是q的最大匹配数不能超过q->val,所以我们还要判断p->ma是否大于q->val,最后要把p->ma赋值为0,攻下一个字符串匹配时使用。当所有的字符串全匹配完之后,我们取所有状态mi的最大值即为所求。代码如下:
#include <iostream> #include <string.h> #include <algorithm> #include <stdio.h> #define maxn 200010 #define inf 210000000 using namespace std; char str[100010]; struct node { node *par,*go[26]; int mi,ma; int val; }*root,*tail,que[maxn],*top[maxn]; int min(int a,int b) { return a<b?a:b; } int max(int a,int b) { return a>b?a:b; } int tot,len,c[maxn]; void add(int c,int l) { node *np=&que[tot++],*p=tail; np->val=l; while(p&&p->go[c]==NULL) { p->go[c]=np; p=p->par; } if(p==NULL) np->par=root; else { node *q=p->go[c]; if(q->val==p->val+1) np->par=q; else { node *nq=&que[tot++]; *nq=*q; nq->val=p->val+1; np->par=q->par=nq; while(p&&p->go[c]==q) { p->go[c]=nq; p=p->par; } } } tail=np; } void init(int n) { for(int i=0;i<=n;i++) { memset(que[i].go,0,sizeof(que[i].go)); que[i].val=0; que[i].mi=inf; que[i].ma=0; } tot=0; len=1; root=tail=&que[tot++]; } void solve() { int i,ans=0,tmp; memset(c,0,sizeof(c)); for(i=0;i<tot;i++) c[que[i].val]++; for(i=1;i<len;i++) c[i]+=c[i-1]; for(i=0;i<tot;i++) top[--c[que[i].val]]=&que[i]; while(scanf("%s",str)!=EOF) { tmp=0; int l=strlen(str); node *p=root; for(i=0;i<l;i++) { int x=str[i]-'a'; if(p->go[x]) { tmp++; p=p->go[x]; } else { while(p&&p->go[x]==NULL) p=p->par; if(p) { tmp=p->val+1; p=p->go[x]; } else { p=root; tmp=0; } } if(p->ma<tmp) p->ma=tmp; } for(i=tot-1;i>=0;i--) { p=top[i]; if(p->mi>p->ma) p->mi=p->ma; if(p->par) { node *q=p->par; q->ma=min(q->val,max(q->ma,p->ma)); } p->ma=0; } } for(i=0;i<tot;i++) ans=max(ans,que[i].mi); printf("%d\n",ans); } int main() { //freopen("dd.txt","r",stdin); scanf("%s",str); int l=strlen(str); init(2*l); for(int i=0;i<l;i++) { add(str[i]-'a',len++); } solve(); return 0; }