【HEOI2015】最短不公共子串

后缀自动机+序列自动机

分别建出A,B两个串的后缀自动机和序列自动机,然后因为后缀自动机和序列自动机都是DAG,所以在上面dp一下就可以啦

dp[ i ][ j ]表示在第一个状态的自动机上匹配到 i 号节点,在第二个状态的自动机上匹配到 j 号节点时  还需要添加dp[ i ][ j ]个字符才能使两串失配(满足条件) ,这个记忆化搜索一下就好了

复杂度:O(N+N*N)

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
using namespace std;
const int inf=1e9+10;
const int maxn=1e4+10;
struct suda
{
	struct da{int ch[30],fa,len;}po[maxn];
	int las,tot;
	void init()
	{
		memset(po,0,sizeof po); 
		las=tot=1;
	}
	void insert(int x)
	{
		int p=las,np=las=++tot; po[np].len=po[p].len+1;
		for (;p&&(!po[p].ch[x]);p=po[p].fa) po[p].ch[x]=np;
		if (!p) po[np].fa=1;
		else 
		{
			int q=po[p].ch[x];
			if (po[q].len==po[p].len+1) po[np].fa=q;
			else
			{
				int nq=++tot; po[nq]=po[q];
				po[nq].len=po[p].len+1;
				po[q].fa=po[np].fa=nq;
				for (;p&&(po[p].ch[x]==q);p=po[p].fa) po[p].ch[x]=nq;
			}
		}
	}
}SAM[3];
struct xuda
{
	struct da{int ch[30];}po[maxn];
	int lc[30];
	void init()
	{
		memset(po,0,sizeof po);
		memset(lc,0,sizeof lc);
	}
	void build(char *s,int n)
	{
		for (int i=n;i>=0;i--)
		{
			for (int j=0;j<26;j++) po[i].ch[j]=lc[j];
			lc[s[i]-'a']=i;
		}
	}
}XUM[3];
int lena,lenb,ans;
char a[maxn],b[maxn];
int dp[4010][4010];
int solve1(int x,int y)
{
	if (dp[x][y]) return dp[x][y];
	int now=inf;
	for (int i=0;i<26;i++)
	if (SAM[1].po[x].ch[i] && SAM[2].po[y].ch[i])
	{
	 	now=min(now,solve1(SAM[1].po[x].ch[i],SAM[2].po[y].ch[i])+1);
	}
	else if (SAM[1].po[x].ch[i] && !SAM[2].po[y].ch[i]) now=1;
	dp[x][y]=now;
return dp[x][y];
}
int solve2(int x,int y)
{
	if (dp[x][y]) return dp[x][y];
	int now=inf;
	for (int i=0;i<26;i++)
	if (SAM[1].po[x].ch[i] && XUM[2].po[y].ch[i])
	{
	 	now=min(now,solve2(SAM[1].po[x].ch[i],XUM[2].po[y].ch[i])+1);
	}
	else if (SAM[1].po[x].ch[i] && !XUM[2].po[y].ch[i]) now=1;
	dp[x][y]=now;
return dp[x][y];
}
int solve3(int x,int y)
{
	if (dp[x][y]) return dp[x][y];
	int now=inf;
	for (int i=0;i<26;i++)
	if (XUM[1].po[x].ch[i] && SAM[2].po[y].ch[i])
	{
	 	now=min(now,solve3(XUM[1].po[x].ch[i],SAM[2].po[y].ch[i])+1);
	}
	else if (XUM[1].po[x].ch[i] && !SAM[2].po[y].ch[i]) now=1;
	dp[x][y]=now;
return dp[x][y];
}
int solve4(int x,int y)
{
	if (dp[x][y]) return dp[x][y];
	int now=inf;
	for (int i=0;i<26;i++)
	if (XUM[1].po[x].ch[i] && XUM[2].po[y].ch[i])
	{
	 	now=min(now,solve4(XUM[1].po[x].ch[i],XUM[2].po[y].ch[i])+1);
	}
	else if (XUM[1].po[x].ch[i] && !XUM[2].po[y].ch[i]) now=1;
	dp[x][y]=now;
return dp[x][y];
}
int main()
{
	scanf("%s%s",a+1,b+1); lena=strlen(a+1); lenb=strlen(b+1);
	for (int i=1;i<=2;i++) SAM[i].init(),XUM[i].init();
	for (int i=1;i<=lena;i++) SAM[1].insert(a[i]-'a');
	XUM[1].build(a,lena);
	for (int i=1;i<=lenb;i++) SAM[2].insert(b[i]-'a');
	XUM[2].build(b,lenb);
	memset(dp,0,sizeof dp); ans=solve1(1,1); printf("%d\n",ans==inf?-1:ans);
	memset(dp,0,sizeof dp); ans=solve2(1,0); printf("%d\n",ans==inf?-1:ans);
	memset(dp,0,sizeof dp); ans=solve3(0,1); printf("%d\n",ans==inf?-1:ans);
	memset(dp,0,sizeof dp); ans=solve4(0,0); printf("%d\n",ans==inf?-1:ans);
return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值