BZOJ2946 [Poi2000]公共串 【后缀自动机】

题目

   给出几个由小写字母构成的单词,求它们最长的公共子串的长度。

任务:
l 读入单词
l 计算最长公共子串的长度
l 输出结果

输入格式

   文件的第一行是整数 n,1<=n<=5,表示单词的数量。接下来n行每行一个单词,只由小写字母组成,单词的长度至少为1,最大为2000。

输出格式

仅一行,一个整数,最长公共子串的长度。

输入样例

3

abcb

bca

acbc

输出样例

2

题解

经典的SAM求多串LCP
首先对第一串建后缀自动机,然后用剩余的每一个串都在后缀自动机上跑一遍,分别得到后缀自动机上每个点的最大匹配长度
最后每个点取每个串匹配长度的最小值,最大的那个点就是答案

但要注意的是在parent树中,儿子的最大匹配值要向父亲传递,因为儿子所代表的位置集合是其父亲的真子集,父亲当然要包括儿子

#include<iostream>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define LL long long int
#define REP(i,n) for (int i = 1; i <= (n); i++)
#define Redge(u) for (int k = h[u],to; k; k = ed[k].nxt)
#define BUG(s,n) for (int i = 1; i <= (n); i++) cout<<s[i]<<' '; puts("");
using namespace std;
const int maxn = 4005,maxm = 100005,INF = 1000000000;
int pre[maxn],ch[maxn][26],step[maxn],cnt,last,n;
int g[maxn],f[maxn],a[maxn],b[maxn];
char s[maxn];
void ins(int x){
	int p = last,np = ++cnt;
	last = np; step[np] = step[p] + 1;
	while (p && !ch[p][x]) ch[p][x] = np,p = pre[p];
	if (!p) pre[np] = 1;
	else {
		int q = ch[p][x];
		if (step[q] == step[p] + 1) pre[np] = q;
		else {
			int nq = ++cnt; step[nq] = step[p] + 1;
			memcpy(ch[nq],ch[q],sizeof(ch[q]));
			pre[nq] = pre[q]; pre[q] = pre[np] = nq;
			while (ch[p][x] == q) ch[p][x] = nq,p = pre[p];
		}
	}
}
void topu(){
	REP(i,cnt) b[step[i]]++;
	REP(i,cnt) b[i] += b[i - 1];
	REP(i,cnt) a[b[step[i]]--] = i;
	REP(i,cnt) f[i] = step[i];
}
void walk(){
	scanf("%s",s + 1); n = strlen(s + 1);
	int u = 1,ans = 0,id;
	REP(i,cnt) g[i] = 0;
	for (int i = 1; i <= n; i++){
		id = s[i] - 'a';
		if (ch[u][id]) u = ch[u][id],g[u] = max(g[u],++ans);
		else {
			while (u != 1 && !ch[u][id]) u = pre[u];
			if (u == 1) ans = 0;
			else ans = step[u] + 1,u = ch[u][id],g[u] = max(g[u],ans);
		}
	}
	for (int i = cnt; i; i--){
		u = a[i];
		if (pre[u]) g[pre[u]] = max(g[pre[u]],g[u]);
		if (f[u] > g[u]) f[u] = g[u];
	}
}
int main(){
	int N; scanf("%d%s",&N,s + 1); N--;
	cnt = last = 1; n = strlen(s + 1);
	REP(i,n) ins(s[i] - 'a');
	topu();
	while (N--) walk();
	int ans = 0;
	REP(i,cnt) ans = max(ans,f[i]);
	printf("%d\n",ans);
	return 0;
}

posted @ 2018-01-18 16:58  Mychael  阅读(124)  评论(0编辑  收藏  举报