BZOJ4032 [HEOI2015]最短不公共子串 【后缀自动机 + 序列自动机 + dp】

题目链接

BZOJ4032

题解

首先膜(hb)
空手切神题
一问(hash),二问枚举
三问(trie)树,四问(dp)
南二巨佬神(hb)
空手吊打自动机
(orz orz orz orz orz orz orz)

咳。说正解
要处理子串,直接搬上后缀自动机
要处理子序列,直接搬上序列自动机【雾】

何为序列自动机##

序列自动机其实很简单,就是具有识别所有子序列功能的自动机
建机原理及过程及其简单,只需要从后往前扫一遍,对每一个位置建立一个节点,该节点的各个边连向最晚出现的对应的字符对应的节点
只需记录下每个字符最晚出现的节点就可以(O(n|s|))建机了 【(|s|)为字符集大小】
代码实现:

struct LAM{
	int ch[maxn][26],last[26],cnt;
	void ins(int x){
		int p = ++cnt;
		for (int i = 0; i < 26; i++)
			ch[p][i] = last[i];
		last[x] = p;
	}
	void init(){
		for (int i = 0; i < 26; i++)
			ch[1][i] = last[i];
	}
}lam;
......
    lam.cnt = 1;
    for (int i = len; i; i--) lam.ins(s[i] - 'a');
    lam.init();

第一、二问##

只需枚举(A)子串的起点,然后看看能在自动机中走多远即可

第三、四问##

(A)的子序列有关,就上(dp)
(f[i][j])表示(A)的前(i)个字符形成的所有子序列匹配到自动机(j)号节点的最短长度
如果(i)不选

[f[i][j] = f[i - 1][j] ]

如果(i)被选,那么枚举是从哪一个节点转移来的
设上一个节点为(u)

[f[i][j] = min{f[i - 1][u] + 1} ]

【注意这里是枚举(u)

答案即为(f[len][0])(0)表示空,即不匹配】

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<map>
#define Redge(u) for (int k = h[u],to; k; k = ed[k].nxt)
#define REP(i,n) for (int i = 1; i <= (n); i++)
#define mp(a,b) make_pair<int,int>(a,b)
#define cls(s) memset(s,0,sizeof(s))
#define cp pair<int,int>
#define LL long long int
using namespace std;
const int maxn = 4005,maxm = 100005,INF = 0x3f3f3f3f;
inline int read(){
	int out = 0,flag = 1; char c = getchar();
	while (c < 48 || c > 57){if (c == '-') flag = -1; c = getchar();}
	while (c >= 48 && c <= 57){out = (out << 3) + (out << 1) + c - 48; c = getchar();}
	return out * flag;
}
struct SAM{
	int ch[maxn][26],pre[maxn],step[maxn],last,cnt;
	void ins(int x){
		int p = last,np = ++cnt; step[np] = step[p] + 1; last = np;
		while (p && !ch[p][x]) ch[p][x] = np,p = pre[p];
		if (!p) pre[np] = 1;
		else {
			int q = ch[p][x];
			if (step[q] == step[p] + 1) pre[np] = q;
			else {
				int nq = ++cnt; step[nq] = step[p] + 1;
				for (int i = 0; i < 26; i++) ch[nq][i] = ch[q][i];
				pre[nq] = pre[q]; pre[np] = pre[q] = nq;
				while (ch[p][x] == q) ch[p][x] = nq,p = pre[p];
			}
		}
	}
}sam;
struct LAM{
	int ch[maxn][26],last[26],cnt;
	void ins(int x){
		int p = ++cnt;
		for (int i = 0; i < 26; i++)
			ch[p][i] = last[i];
		last[x] = p;
	}
	void init(){
		for (int i = 0; i < 26; i++)
			ch[1][i] = last[i];
	}
}lam;
char A[maxn],B[maxn];
int lena,lenb;
void solve1(){
	int ans = INF;
	for (int i = 1,j; i <= lena; i++){
		int u = 1,cnt = 0;
		for (j = i; j <= lena; j++){
			++cnt;
			if (!sam.ch[u][A[j] - 'a']) break;
			u = sam.ch[u][A[j] - 'a'];
		}
		if (j <= lena) ans = min(ans,cnt);
	}
	if (ans == INF) puts("-1");
	else printf("%d
",ans);
}
void solve2(){
	int ans = INF;
	for (int i = 1,j; i <= lena; i++){
		int u = 1,cnt = 0;
		for (j = i; j <= lena; j++){
			++cnt;
			if (!lam.ch[u][A[j] - 'a']) break;
			u = lam.ch[u][A[j] - 'a'];
		}
		if (j <= lena) ans = min(ans,cnt);
	}
	if (ans == INF) puts("-1");
	else printf("%d
",ans);
}
int f[2005][maxn];
void solve3(){
	memset(f,INF,sizeof(f));
	f[0][1] = 0;
	for (int i = 1; i <= lena; i++){
		for (int j = 1; j <= sam.cnt; j++) f[i][j] = min(f[i][j],f[i - 1][j]);
		for (int j = 1; j <= sam.cnt; j++){
			int to = sam.ch[j][A[i] - 'a'];
			f[i][to] = min(f[i][to],min(f[i - 1][to],f[i - 1][j] + 1));
		}
	}
	if (f[lena][0] == INF) puts("-1");
	else printf("%d
",f[lena][0]);
}
void solve4(){
	memset(f,INF,sizeof(f));
	f[0][1] = 0;
	for (int i = 1; i <= lena; i++){
		for (int j = 1; j <= lam.cnt; j++) f[i][j] = min(f[i][j],f[i - 1][j]);
		for (int j = 1; j <= lam.cnt; j++){
			int to = lam.ch[j][A[i] - 'a'];
			f[i][to] = min(f[i][to],min(f[i - 1][to],f[i - 1][j] + 1));
		}
	}
	if (f[lena][0] == INF) puts("-1");
	else printf("%d
",f[lena][0]);
}
int main(){
	scanf("%s",A + 1); lena = strlen(A + 1);
	scanf("%s",B + 1); lenb = strlen(B + 1);
	sam.last = sam.cnt = 1; lam.cnt = 1;
	for (int i = 1; i <= lenb; i++) sam.ins(B[i] - 'a');
	for (int i = lenb; i; i--) lam.ins(B[i] - 'a');
	lam.init();
	solve1();
	solve2();
	solve3();
	solve4();
	return 0;
}

原文地址:https://www.cnblogs.com/Mychael/p/9039976.html