后缀自动机--bzoj4032: [HEOI2015]最短不公共子串

传送门

一个题=四个题

T1

这就是对于两个串上的问题通常套路就是对一个建后缀自动机另一个在上面跑,所以对 B B B建出后缀自动机然后枚举 A A A的子串开头跑就行了,复杂度 O ( n 2 ) O(n^2) O(n2)

T2

有一个东西叫做序列自动机,但其实一点也不自动,也没有自动机该有的样子
对一个串从后往前记录每个字符出现的最近位置,然后用一个 p r e [ i ] [ j ] pre[i][j] pre[i][j]表示 i i i位置往后接一个 j j j的下一个位置在哪,这样的话从一个位置就可以知道接上一个字符 c c c以后最前的位置,因为越靠前一定可以接更多的字符,然后就是枚举 A A A的子串开头,然后放到 B B B的序列自动机上面跑就好啦,复杂度 O ( n 2 ) O(n^2) O(n2)

T3

这个其实就是, A A A在序列自动机上转移,跑 B B B的后缀自动机,原理就是上面那个原理,复杂度是 O ( n 2 × 26 ) O(n^2\times 26) O(n2×26)

T4

就是两个序列自动机一起跑,但这样复杂度好像很爆炸因为它会转移到所有状态然而加个记忆化就过了因为一种状态只搜了一次,复杂度 O ( n 2 × 26 ) O(n^2\times 26) O(n2×26)

代码如下:
(常数优秀,虽然改的比较乱

#include
#include
#include
#include
#include
#define N 2005
#define C 30
using namespace std;

int n,m,la,lb,ans,nxt[N],prea[N][C],preb[N][C],vis[N][N];
char a[N],b[N];

inline int min(int x,int y){return x<y?x:y;}

struct SAM{
	int lst,cnt,ch[N<<1][C],fa[N<<1],l[N<<1],siz[N<<1];
	inline void insert(int c){
		int p=lst,np=++cnt; lst=np; l[np]=l[p]+1;
		while(p&&!ch[p][c]) ch[p][c]=np,p=fa[p];
		if(!p) fa[np]=1;
		else{
			int q=ch[p][c];
			if(l[p]+1==l[q]) fa[np]=q;
			else{
				int nq=++cnt; l[nq]=l[p]+1;
				memcpy(ch[nq],ch[q],sizeof ch[q]);
				fa[nq]=fa[q],fa[q]=fa[np]=nq;
				while(p&&ch[p][c]==q) ch[p][c]=nq,p=fa[p];
			}
		} siz[np]++;
	}
	inline void build(int len,char *p){
		cnt=lst=1; for(int i=1;i<=len;i++) insert(p[i]-'a');
	}
}sam;

void dfs1(int pa,int pb,int len){
	if(ans<=len+1 || pa==la) return;
	int c=a[pa+1]-'a';
	if(sam.ch[pb][c]) dfs1(pa+1,sam.ch[pb][c],len+1);
	else {ans=min(ans,len+1);return;}
}

inline void solve1(){
	ans=la+1;
	for(int i=0;i<la;i++) dfs1(i,1,0);
	printf("%d\n",ans<=la?ans:-1);
}

void dfs2(int pa,int pb,int len){
	if(ans<=len+1 || pa==la) return;
	int c=a[pa+1]-'a';
	if(preb[pb][c]) dfs2(pa+1,preb[pb][c],len+1);
	else {ans=min(ans,len+1);return;}
}

inline void solve2(){
	ans=la+1;
	for(int i=0;i<la;i++) dfs2(i,0,0);
	printf("%d\n",ans<=la?ans:-1);
}

void dfs3(int pa,int pb,int len){
	if(ans<=len+1 || pa==la) return;
	for(int i=0;i<26;i++)
		if(prea[pa][i]){
			if(sam.ch[pb][i]) dfs3(prea[pa][i],sam.ch[pb][i],len+1);
			else {ans=min(ans,len+1);return;}
		}
}

inline void solve3(){
	ans=la+1; dfs3(0,1,0);
	printf("%d\n",ans<=la?ans:-1);
}

int dfs4(int pa,int pb,int len){
	if(pb==lb) return len+1;
	if(vis[pa][pb]) return len+vis[pa][pb];
	int t=la+1;
	for(int i=0;i<26;i++)
		if(prea[pa][i]){
			if(preb[pb][i]) t=min(dfs4(prea[pa][i],preb[pb][i],1),t);
			else t=min(t,1);
		}
	return len+(vis[pa][pb]=t);
}

inline void solve4(){
	ans=la+1; ans=dfs4(0,0,0);
	printf("%d\n",ans<=la?ans:-1);
}

int main(){
	scanf("%s%s",a+1,b+1); la=strlen(a+1),lb=strlen(b+1);
	sam.build(lb,b);
	for(int i=la;i>=0;i--){
		for(int j=0;j<26;j++)
			prea[i][j]=nxt[j];
		if(i) nxt[a[i]-'a']=i;
	}
	memset(nxt,0,sizeof nxt);
	for(int i=lb;i>=0;i--){
		for(int j=0;j<26;j++)
			preb[i][j]=nxt[j];
		if(i) nxt[b[i]-'a']=i;
	}
	solve1(); solve2(); solve3(); solve4();
	return 0;
}

你可能感兴趣的:(序列自动机,后缀自动机,后缀自动机,序列自动机)