SPOJ - LCS2 后缀自动机

解题思路:多个串的最长公共子串后缀自动机,把一个串建成树,因为根节点到每个节点都是他的子串,那么去取每个节点也就是该串的每个子串与其他串的相同长度,用mn[i]表示该节点表示的子串和其他串相同的最小长度,然后最后取从1-tot节点中取max就可以了。


#include<bits/stdc++.h>
#define inf 0x3f3f3f3f
using namespace std;
typedef long long ll;
const int mx = 1e5 + 10;
char str[mx],Ts[mx];
struct state
{
	int fa,len;//fa为包含该转态的最相近节点,len是从根节点走到该转态的最长长度 
	int id[26];	
}Tr[2*mx];
int tot,last,ans,cnt[2*mx],v[2*mx],pos[2*mx];
int mn[mx<<1];
//后缀自动机定义,从根节点出发到达任意一个节点形成的串都是唯一的,且一定是母串的子串 
void suf_auto(int ch)
{
	int np = ++tot,p = last;
	//其实last就是原来的一条直串扩展到的前一个位置
	//比如abccbdab这个串,自动机上肯定存在最长的一个路径就是这个串,那么last就是一直在这上面的
	Tr[np].len = Tr[last].len + 1;
	last = np;
	//for(;p&&!Tr[p].id[ch];p=Tr[p].fa) Tr[p].id[ch] = np;
	while(p&&!Tr[p].id[ch]) Tr[p].id[ch] = np, p = Tr[p].fa; 
	//找到包含p转态的节点,那么它也可以转移到新转态np 
	if(!p) Tr[np].fa = 1;//如果都没有包含p状态的节点有可以扩展出ch这个转态,那么新状态被根节点包含 
	else{
		int q = Tr[p].id[ch];
		//如果if成立,不存在另一个点t使得Tr[t].len + 1==Tr[q].len证明也很简单 
		if(Tr[p].len+1==Tr[q].len) Tr[np].fa = q;//说明q状态可以包含新状态np 
		else{
			int nq = ++tot; 
			Tr[nq] = Tr[q];//分裂出q转态,就是分裂出if判断那个节点
			Tr[nq].len = Tr[p].len + 1;
			Tr[q].fa = Tr[np].fa = nq;//分裂出的nq肯定可以包含原来的点q和np 
			while(p&&Tr[p].id[ch]==q) Tr[p].id[ch] = nq,p = Tr[p].fa;
			//将后面包含p的点的儿子是q的也变成分裂的那个,因为他们就是跟着p走的,分裂出来的nq自然是p直接扩展状态 
		}
	}
}
void init()//初始化根节点为1号 
{
	memset(mn,inf,sizeof(mn));
	last = tot = 1; 
	Tr[1].len = Tr[1].fa = 0;
	//memset(Tr[1].id,0,sizeof(Tr[1].id));
	//这里注意多个数据id数组要初始化 
}
void find(int len)
{
	memset(cnt,0,sizeof(cnt));
	int ret = 0,p = 1;
	for(int i=0;i<len;i++){
		int ch = Ts[i] - 'a';
		if(Tr[p].id[ch]) ret++,p = Tr[p].id[ch];
		else{
			while(p&&!Tr[p].id[ch]) p = Tr[p].fa;
			if(p) ret = Tr[p].len + 1,p = Tr[p].id[ch]; 
			else p = 1,ret = 0;
		}
		cnt[p] = max(ret,cnt[p]);
 	}
 	for(int i=tot;i>=1;i--){
 		int po = pos[i];
 		mn[po] = min(mn[po],cnt[po]);
 		if(cnt[po]) cnt[Tr[po].fa] = Tr[Tr[po].fa].len;//说明pre节点任意状态都可以匹配
	 }
}
int main()
{
	init();
	scanf("%s",str);
	int len = strlen(str);
	for(int i=0;i<len;i++) suf_auto(str[i]-'a');
	for(int i=1;i<=tot;i++) v[Tr[i].len]++;//这里要拓扑排序一下,因为有可能新节点是旧的pre,旧的也可能是新的pre 
	for(int i=1;i<=tot;i++) v[i] += v[i-1];//根据节点最长子串长度排,这样就会先考虑后继然后再去更新前驱 
	for(int i=1;i<=tot;i++)  pos[v[Tr[i].len]--] = i;
	while(~scanf("%s",Ts)){
		len = strlen(Ts);
		find(len);
	}
	for(int i=1;i<=tot;i++) ans = max(mn[i],ans); 
	printf("%d\n",ans); 
	return 0;
} 


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值