[HEOI2015]最短不公共子串

Posted by Dispwnl on January 6, 2019

题目

题目描述

在虐各种最长公共子串、子序列的题虐的不耐烦了之后,你决定反其道而行之。

一个串的“子串”指的是它的连续的一段,例如bcd是abcdef的子串,但bde不是。

一个串的“子序列”指的是它的可以不连续的一段,例如bde是abcdef的子串,但bdd不是。

下面,给两个小写字母串A,B,请你计算:

(1) A的一个最短的子串,它不是B的子串

(2) A的一个最短的子串,它不是B的子序列

(3) A的一个最短的子序列,它不是B的子串

(4) A的一个最短的子序列,它不是B的子序列

输入输出格式

输入格式:

有两行,每行一个小写字母组成的字符串,分别代表A和B。

输出格式:

输出4行,每行一个整数,表示以上4个问题的答案的长度。如果没有符合要求的答案,输出-1.

输入输出样例

输入样例#1:

aabbcc
abcabc

输出样例#1:

2
4
2
4

输入样例#2:

aabbcc
aabbcc

输出样例#2:

-1
-1
2
-1

说明

对于20%的数据,A和B的长度都不超过20

对于50%的数据,A和B的长度都不超过500

对于100%的数据,A和B的长度都不超过2000

题解

1.直接在$B$的$SAM$上跑即可

2.$O(n^2)$处理出$B$每个位置下一个字符$x$的位置,然后从左到右贪心即可

3.$f_i$表示$B$的$SAM$上以节点$i$结尾的最短长度,从左到右枚举$A$的字符,依次更新$f$,相当于一个递推的 过程

4.$f_i$表示$B$字符串上以$i$位置结尾的最短长度,跟问题$3$一样处理,只是倒着枚举$SAM$节点

代码

# include<iostream>
# include<cstring>
# include<cstdio>
# include<algorithm>
using namespace std;
const int MAX=4e3+5;
int n,_n;
char a[MAX],b[MAX];
struct SAM{
	int l,r,L;
	int len[MAX],fa[MAX],f[MAX];
	int son[MAX][26],nxt[MAX][26];
	SAM() {l=r=1;}
	void ins(int x)
	{
		int tt=r;
		len[r=++l]=++L;
		for(;tt&&!son[tt][x];tt=fa[tt])
		  son[tt][x]=r;
		if(!tt) return void(fa[r]=1);
		int qwq=son[tt][x];
		if(len[qwq]==len[tt]+1) return void(fa[r]=qwq);
		len[++l]=len[tt]+1,fa[l]=fa[qwq],fa[qwq]=fa[r]=l;
		for(int i=0;i<26;++i)
		  son[l][i]=son[qwq][i];
		for(int i=tt;son[i][x]==qwq;i=fa[i])
		  son[i][x]=l;
	}
	void Solve()
	{
		int ans=1e9;
		for(int i=1,N;i<=n;++i)
		  {
		  	N=min(i+ans-1,n);
		  	for(int j=i,x=1;j<=N;++j)
		  	  {
		  	  	x=son[x][a[j]-'a'];
		  	  	if(!x) {ans=j-i+1;break;}
			  }
		  }
		printf("%d\n",ans==1e9?-1:ans);
	}
	void Solve_()
	{
		int ans=1e9;
		for(int i=0;i<_n;++i)
		  for(int j=i+1;j<=_n;++j)
		    if(!nxt[i][b[j]-'a']) nxt[i][b[j]-'a']=j;
		for(int i=1,N;i<=n;++i)
		  {
		  	N=min(i+ans-1,n);
		  	for(int j=i,x=0;j<=N;++j)
		  	  {
		  	  	x=nxt[x][a[j]-'a'];
		  	  	if(!x) {ans=j-i+1;break;}
			  }
		  }
		printf("%d\n",ans==1e9?-1:ans);
	}
	void Solve__()
	{
		int ans=1e6;
		memset(f,1,sizeof(f));
		f[1]=0;
		for(int i=1;i<=n;++i)
		  for(int j=1,x;j<=l;++j)
		    {
		    	x=son[j][a[i]-'a'];
		    	if(!x) ans=min(ans,f[j]+1);
		    	else f[x]=min(f[x],f[j]+1);
			}
		printf("%d\n",ans==1e6?-1:ans);
	}
	void Solve___()
	{
		int ans=1e6;
		memset(f,1,sizeof(f));
		f[0]=0;
		for(int i=1;i<=n;++i)
		  for(int j=_n,x;j>=0;--j)
		    {
		    	x=nxt[j][a[i]-'a'];
		    	if(!x) ans=min(ans,f[j]+1);
		    	else f[x]=min(f[x],f[j]+1);
			}
		printf("%d\n",ans==1e6?-1:ans);
	}
}_S;
int main()
{
	scanf("%s%s",a+1,b+1),n=strlen(a+1),_n=strlen(b+1);
	for(int i=1;i<=_n;++i)
	  _S.ins(b[i]-'a');
	return _S.Solve(),_S.Solve_(),_S.Solve__(),_S.Solve___(),0;
}