1. 程式人生 > >【再談字尾自動機(入門)】[SPOJLCS2]Longest Common Substring II

【再談字尾自動機(入門)】[SPOJLCS2]Longest Common Substring II

題目大意

給出n個字串n<=10,每個字串長度小於100000,求它們的最長公共串的長度。

分析

關於字尾自動機

複習過程中再看字尾自動機,把許多初學的時候沒弄懂的問題解決了。
首先要明確字尾自動機的每個節點所表示的是一個終點等價類,從根節點走到葉子節點就對應一個字尾,len表示這終點等價類中長度最長字串的長度,這個中終點等價類中的字串的長度連續,且長度短字串的是長度長的的字尾,字尾邊指向的節點所對應的字串是這個節點對應的字串的字尾。
字尾邊所指向的節點所對應的終點等價類的終點包含當前節點的終點等價類對應的終點。

ufail=vendpos(u)endpos(v)len(v)ma
x
+1=len(u)min

在構建的過程中plast沿著字尾邊走第一個有字元c轉移的節點,qp經字元c轉移而來的。我們要使q所對應的終點等價類增加c這個位置。
  • q>lenp>len+1,說明由p轉移到q並不是q點所對應的最長的字串但這卻是能夠增加這個終點的最長的字串,q所對應的長於該長度的字串都不能增加這個終點,,所以我們新建一個節點nq,將q的資訊複製給他len,fail,chnqlen=plen+1qfial=nq,npfial=nq,並且沿著字尾邊走,將所有有字元c轉移且轉移到q的轉移重定向至nq
    為什麼ch也要複製?
    因為這個節點對應的字串加上轉移的邊所對應的字元形成的字串的終點和這個節點的終點的沒有關係,所以要保證這個節點的兒子節點所對應的字串不變。
  • q>len=p>len+1,則npfial=q

這道題

每個節點儲存兩個值:當前字串能夠匹配這個節點對應的字串的最大長度nmx,所有字串能夠匹配這個節點對應的字串的最大長度的最小值mx
然後,每次匹配完之後要沿著字尾邊更新nmx,否則在用nmx更新mx時會出錯。

ans=max(pmx)

程式碼

#include<cstdio>
#include<cstring>
#include<algorithm>
#define INF 0x7fffffff
#define MAXN 100000
using namespace std;
void Read(int
&x){ char c; while(c=getchar(),c!=EOF) if(c>='0'&&c<='9'){ x=c-'0'; while(c=getchar(),c>='0'&&c<='9') x=x*10+c-'0'; ungetc(c,stdin); return; } } struct node{ int len,mx,nmx; node *ch[26],*fail; }tree[MAXN*2+10],*tcnt=tree,*root=tcnt,*l=tcnt,*r[MAXN*2+10]; char s[MAXN+10]; int ans,b[MAXN+10]; void insert(char c){ c-='a'; node *p=l,*np=++tcnt,*q,*nq; l=np; np->len=p->len+1; while(p&&!p->ch[c]) p->ch[c]=np,p=p->fail; if(!p) np->fail=root; else{ q=p->ch[c]; if(q->len==p->len+1) np->fail=q; else{ nq=++tcnt; *nq=*q; nq->len=p->len+1; q->fail=nq; np->fail=nq; while(p&&p->ch[c]==q) p->ch[c]=nq,p=p->fail; } } } void read(){ scanf("%s",s); for(int i=0;s[i];i++) insert(s[i]); } void solve(){ int i,len,tot; node *p; for(p=tree;p<=tcnt;p++){ p->mx=p->len,p->nmx=0; b[p->len]++; } for(i=1;s[i-1];i++) b[i]+=b[i-1]; for(p=tree;p<=tcnt;p++) r[--b[p->len]]=p; tot=tcnt-tree; while(~scanf("%s",s)){ p=root; len=0; for(i=0;s[i];i++){ s[i]-='a'; if(p->ch[s[i]]){ p=p->ch[s[i]]; p->nmx=max(p->nmx,++len); } else{ while(p&&!p->ch[s[i]]) p=p->fail; if(!p) p=root,len=0; else{ len=p->len+1; p=p->ch[s[i]]; p->nmx=max(p->nmx,len); } } } for(i=tot;i;i--){ r[i]->fail->nmx=max(r[i]->nmx,r[i]->fail->nmx);//不用和r[i]->len進行比較,因為不會影響更新r[i]->mx r[i]->mx=min(r[i]->mx,r[i]->nmx); r[i]->nmx=0; } } for(p=tree;p<=tcnt;p++) ans=max(ans,p->mx); } int main() { read(); solve(); printf("%d\n",ans); }