1. 程式人生 > >最長公共子串 字尾自動機

最長公共子串 字尾自動機

【題目描述】

給定兩個字串A和B,求它們的最長公共子串

【分析】

我們考慮將A串建成字尾自動機

令當前狀態為s,同時最大匹配長度為len

我們讀入字元x。如果s有標號為x的邊, 那麼s=trans(s,x),len = len+1

否則我們找到s的第一個祖先a,它有標號為x的邊,令 s=trans(a,x),len=Max(a)+1。

如果沒有這樣的祖先,那麼令s=root,len=0。

在過程中更新狀態的最大匹配長度

注意到我們求的是對於任意一個Right集合中的r,最大的匹配長度。那麼對於一個狀態s,它的結果自然也可以作為它Parent的結果,我們可以從底到上更新一遍。

然後問題就解決了。

———來自陳立傑《字尾自動機講稿》

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<ctime>
#include<cmath>
#include<algorithm>
using namespace std;
#define FILE "read"
#define MAXN 200010
#define up(i,j,n) for(int i=j;i<=n;++i)
#define dn(i,j,n) for(int i=j;i>=n;--i)
#define cmax(a,b) a=max(a,b)
#define cmin(a,b) a=min(a,b)
char ch[MAXN];
int n,cnt(1),now(1),len,ans,mx[MAXN],par[MAXN],son[MAXN][27];
void insert(int x){
	int p=now,np=++cnt;
	mx[np]=mx[now]+1;  now=np;
	while(p&&!son[p][x]) son[p][x]=np,p=par[p];
	if(!p) par[np]=1;
	else{
		int q=son[p][x];
		if(mx[q]==mx[p]+1) par[np]=q;
		else{
			int nq=++cnt;
			mx[nq]=mx[p]+1;
			memcpy(son[nq],son[q],sizeof(son[q]));
			par[nq]=par[q];
			par[q]=par[np]=nq;
			while(p&&son[p][x]==q)son[p][x]=nq,p=par[p];
		}
	}
}
int walk(int x){
	while(!son[now][x]&&par[now]){
		now=par[now];
		len=mx[now];
	}
	if(!son[now][x]) return 0;
	now=son[now][x];  len++;
	return len;
}
int main(){
	freopen(FILE".in","r",stdin);
	freopen(FILE".out","w",stdout);
	scanf("%s",ch+1);  n=strlen(ch+1);
	up(i,1,n)  insert(ch[i]-'a');
	scanf("%s",ch+1);  n=strlen(ch+1);  now=1;
	up(i,1,n)  cmax(ans,walk(ch[i]-'a'));
	printf("%d\n",ans);
	return 0;
}