http://codeforces.com/problemset/problem/127/D
題意:求一個字串S的最長子串,使得它同時是S的首碼,尾碼和中綴。
思路:這道題有多種方法,這裡介紹SAM的方法,我們先構造S的SAM,對於SAM的每一個狀態,設num為其所代表字串在S中出現的個數,r為其代表子串在S中出現的最右位置(這兩個量由SAM的性質可以拓撲排序後自底向上求出)。然後用S在SAM上做匹配。假設我們已經匹配了長度n,由於當前匹配的串一定是S的首碼,我們只要判定它是否為中綴和尾碼,什麼樣的串是S的尾碼呢?顯然是在S中出現位置為|S|的子串,則假若當前走到狀態p,則若p->r==|S|,說明它一定是S的尾碼,判定是否為中綴只要看它在S中出現次數大於3即可。則我們一次匹配後,最後一個滿足前兩個要求的子串即為所求,若沒有則輸出沒有。代碼如下:
#include <iostream>#include <string.h>#include <stdio.h>#define maxn 2000010#define Smaxn 26#define inf 21000000using namespace std;struct node{ node *par,*go[Smaxn]; int right; int num; int po; int val;}*root,*tail,que[maxn],*top[maxn];int tot;char str[maxn>>1];void add(int c,int l,int po){ node *p=tail,*np=&que[tot++]; np->val=l; np->po=po; 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(p->val+1==q->val) 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;}int c[maxn],len;void init(){ memset(que,0,sizeof(que)); tot=0; len=1; root=tail=&que[tot++];}void solve(int n){ memset(c,0,sizeof(c)); int i; 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]; for(node *p=root;;p=p->go[str[p->val+1]-'a']) { p->num=1; p->right=p->po; //printf("f"); if (p->val==len-1)break; } for(i=tot-1;i>=0;i--) { node *p=top[i]; if(p->right==0) { p->right=p->po; } if(p->par) { node *q=p->par; q->num+=p->num; if(q->right==0||q->right<p->right) q->right=p->right; } } int po=0; node *p=root; for(i=1;i<=n;i++) { p=p->go[str[i]-'a']; if(str[i]==str[n]) { if(p->right==n&&p->num>=3) po=i; } } if(po==0) printf("Just a legend\n"); else { for(i=1;i<=po;i++) printf("%c",str[i]); printf("\n"); }}int main(){ freopen("dd.txt","r",stdin); scanf("%s",str+1); int i,l=strlen(str+1); init(); for(i=1;i<=l;i++) { add(str[i]-'a',len++,i); } solve(l); return 0;}