【spoj LCS2】 Longest Common Substring II

http://www.spoj.com/problems/LCS2/ (题目链接)

题意

  求多个串的最长公共子串

Solution

  对其中一个串构造后缀自动机,然后其它串在上面跑匹配。对于每个串都可以跑出在SAM上的每一个节点的最长公共子串的长度,当然,有些节点虽然匹配时可能没有经过,但是在parent树上它的儿子却被经过了,作为儿子的后缀,那么这些节点显然也是被经过的,所以我们需要用parent树上的儿子节点去更新其父亲节点。完成之后,我们再对全局的匹配长度进行更新(取min)。

  爱神:对于SAM初学,要深刻理解出现次数向父亲传递,接收串数从儿子获取这句话。这里的父亲是parent树上的父亲,儿子是SAM图上的后继节点。

代码

// spoj LCS2
#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<vector>
#include<cstdio>
#include<cmath>
#include<set>
#define LL long long
#define inf 1<<30
#define Pi acos(-1.0)
#define free(a) freopen(a".in","r",stdin),freopen(a".out","w",stdout);
using namespace std;
 
const int maxn=100010;
char s[maxn];

namespace SAM {
	int Dargen,sz,last,n;
	int par[maxn<<1],len[maxn<<1],ch[maxn<<1][26],mat[maxn<<1],f[maxn<<1];
	int b[maxn],id[maxn<<1];
	void Extend(int c) {
		int np=++sz,p=last;last=np;
		len[np]=len[p]+1;
		for (;p && !ch[p][c];p=par[p]) ch[p][c]=np;
		if (!p) par[np]=Dargen;
		else {
			int q=ch[p][c];
			if (len[q]==len[p]+1) par[np]=q;
			else {
				int nq=++sz;len[nq]=len[p]+1;
				memcpy(ch[nq],ch[q],sizeof(ch[q]));
				par[nq]=par[q];
				par[np]=par[q]=nq;
				for (;p && ch[p][c]==q;p=par[p]) ch[p][c]=nq;
			}
		}
	}
	void build() {
		last=sz=Dargen=1;
		n=strlen(s+1);
		for (int i=1;i<=n;i++) Extend(s[i]-'a');
	}
	void pre() {
		for (int i=1;i<=sz;i++) b[len[i]]++;
		for (int i=1;i<=n;i++) b[i]+=b[i-1];
		for (int i=1;i<=sz;i++) id[b[len[i]]--]=i;
		for (int i=1;i<=sz;i++) mat[i]=inf;
	}
	void match() {
		int n=strlen(s+1);
		int ll=0;
		for (int i=1;i<=sz;i++) f[i]=0;
		for (int p=Dargen,i=1;i<=n;i++) {
			while (p>1 && !ch[p][s[i]-'a']) p=par[p],ll=len[p];
			if (ch[p][s[i]-'a']) {
				p=ch[p][s[i]-'a'];
				f[p]=max(f[p],++ll);
			}
		}
		for (int i=sz;i>=1;i--)
			if (f[id[i]]) f[par[id[i]]]=len[par[id[i]]];
		for (int i=1;i<=sz;i++) mat[i]=min(mat[i],f[i]);
	}
	int query() {
		int ans=0;
		for (int i=1;i<=sz;i++) ans=max(ans,mat[i]);
		return ans;
	}
}
using namespace SAM;

int main() {
	scanf("%s",s+1);
	build();
	pre();
	while (scanf("%s",s+1)!=EOF) match();
	printf("%d",query());
	return 0;
}
原文地址:https://www.cnblogs.com/MashiroSky/p/6288525.html