阅读

题意

\(n\) 个串,每次可以从每一个串中取出前缀,但每一次的取出的所有前缀不满足被包含或包含关系, 求取出所有前缀的最小次数,\(\sum l \leq 10^5\)。

变图, AC自动机

能发现一个串中最多能取出一个前缀,如果取出两个前缀,那么一定其中短的前缀是长的前缀的子串。

如果一个串的前缀能包含另一个串的前缀,那么就可以连一条有向边。

对于每个串都能对应一个点,如果在图中一点能到另一点,那么它们就一定是子串关系。

这样的图刚好和 AC自动机 中的 trie 树和 fail边所构成的图对应。

最小反链覆盖

图上某个点集,满足两两点集互不相交,称为反链。

这里要求的是AC自动机上的图的最小反链覆盖。

有个定理:在有向无环图中,最小反链覆盖 = 最长链的长度。

因此建出AC自动机后连fail边,跑DP即可。

代码

#include<bits/stdc++.h>

using namespace std;

using ll = long long;
const int MAXN = 1000010;
const int INF = 0x7fffffff;
const int mod = 998244353;

template <typename T>
void Read(T &x) {
	x = 0; T f = 1; char a = getchar();
	for(; a < '0' || '9' < a; a = getchar()) if (a == '-') f = -f;
	for(; '0' <= a && a <= '9'; a = getchar()) x = (x * 10) + (a ^ 48);
	x *= f;
}

int n, root, cnt; 
int trie[MAXN][27], fail[MAXN], deg[MAXN], tot[MAXN];

void Insert(char *a) {
	int cur = root, n = strlen(a);
	for (int i = 0; i < n; i ++) {
		if (!trie[cur][a[i] - 'a']) trie[cur][a[i] - 'a'] = ++ cnt;
		cur = trie[cur][a[i] - 'a'];
		tot[cur] ++; 
	}
}

vector <int> e[MAXN];
void build() {
	queue<int> q; 
	for (int i = 0; i < 26; i ++)
		if (trie[root][i]) {
			q.push(trie[root][i]); 
		}
	while (!q.empty()) {
		int u = q.front(); q.pop();
		for (int i = 0; i < 26; i ++) {
			if (trie[u][i]) fail[trie[u][i]] = trie[fail[u]][i], q.push(trie[u][i]), e[u].emplace_back(trie[u][i]), deg[trie[u][i]] ++;
			else trie[u][i] = trie[fail[u]][i]; 
		}
	}
	for (int i = 1; i <= cnt; i ++)
		if (fail[i])
			e[fail[i]].emplace_back(i), deg[i] ++;
}

int f[MAXN]; 
char a[MAXN]; 
int main() {
	ios::sync_with_stdio(false);
	cin >> n; 
	for (int i = 1; i <= n; i ++) {
		cin >> a; 
		Insert(a); 
	}
	build();
	queue<int> q; 
	for (int i = 1; i <= cnt; i ++)
		if (!deg[i]) q.push(i), f[i] = 1; 
	while (!q.empty()) {
		int u = q.front(); q.pop();
		for (auto v : e[u]) {
			f[v] = max(f[v], f[u] + tot[u]); 
			if (!-- deg[v]) {
				q.push(v); 
			}
		}
	} 
	int ans = 0;
	for (int i = 1; i <= cnt; i ++)
		ans = max(ans, f[i]);
	cout << ans; 
	return 0;
} 
上一篇:20220228-java基础


下一篇:opencv读取并播放avi视屏