SP1812-LCS2-后缀自动机

本文深入解析了SP1812-LCS2后缀自动机问题,通过构建SAMSAM后缀自动机,实现字符串匹配并计算最长公共子串。文章详细介绍了如何使用拓扑排序更新节点最大匹配值,最终求得所有串的最长公共子串长度。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

SP1812-LCS2-后缀自动机

题目描述

题目描述

题解

对于一个串构建 S A M SAM SAM
每个串依次进行匹配
同时记录 t r [ i ] . m a x n tr[i].maxn tr[i].maxn表示走到了 i i i节点能够匹配上的最长公共子串的长度
当然,每个串的tr[i].maxn可以更新 t r [ t r [ i ] . f a ] . m a x n tr[tr[i].fa].maxn tr[tr[i].fa].maxn
所以需要拓扑排序
对于每个串求出每个节点的最长匹配
然后对他们取 m i n min min,表示某个节点大家都能匹配的最长长度
最后对于所有点的值都取个 m a x max max就是答案

代码

#include<bits/stdc++.h>
#define M 100009
using namespace std;
struct sam{
	int fa,len,minx,maxn,ch[26];
}tr[M*4];
int ans,last,cnt,rt,vis[M*4],id[M*4];//后缀自动机相关的数组要开2倍以上
char s[M];
void extend(int pos){//构造后缀自动机
	int np=++cnt,p=last,val=s[pos]-'a';
	last=np,tr[np].len=pos;
	while(p&&!tr[p].ch[val]) tr[p].ch[val]=np,p=tr[p].fa;
	if(p==0) tr[np].fa=rt;
	else{
		int q=tr[p].ch[val];
		if(tr[q].len==tr[p].len+1) tr[np].fa=q;
		else{
			int nq=++cnt;
			tr[nq].len=tr[p].len+1;
			memcpy(tr[nq].ch,tr[q].ch,sizeof(tr[q].ch));
			tr[nq].fa=tr[q].fa,tr[np].fa=tr[q].fa=nq;
			while(p&&tr[p].ch[val]==q) tr[p].ch[val]=nq,p=tr[p].fa;
		}
	}
}
int getans(){
	int n=strlen(s+1),p=rt,len=0;
	for(int i=1;i<=n;i++){//此处与两个串的匹配方式相同
		int val=s[i]-'a';
		if(tr[p].ch[val]) len++,p=tr[p].ch[val];
		else{
			while(p&&!tr[p].ch[val]) p=tr[p].fa;
			if(p==0) len=0,p=rt;
			else len=tr[p].len+1,p=tr[p].ch[val];
		}tr[p].maxn=max(tr[p].maxn,len);
	}
	for(int i=cnt;i>=1;i--){//由拓扑序更新
		int v=id[i];
		tr[v].minx=min(tr[v].minx,tr[v].maxn);
		if(tr[v].fa&&tr[v].maxn>=tr[v].len)
			tr[tr[v].fa].maxn=tr[tr[v].fa].len;
	}
}
int main(){
	scanf("%s",s+1);
	int n=strlen(s+1);last=rt=++cnt;
	for(int i=1;i<=n;i++) extend(i);
	for(int i=1;i<=cnt;i++) vis[tr[i].len]++;
	for(int i=1;i<=cnt;i++) vis[i]+=vis[i-1];
	for(int i=1;i<=cnt;i++) id[vis[tr[i].len]--]=i;//O(n)的拓扑排序
	for(int i=1;i<=cnt;i++) tr[i].minx=tr[i].len;//赋初始值
	while(~scanf("%s",s+1)){
		for(int i=1;i<=cnt;i++) tr[i].maxn=0;//记得清空maxn
		getans();	
	}for(int i=1;i<=cnt;i++) ans=max(ans,tr[i].minx);
	printf("%d\n",ans);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值