changeeksdja

这几天都在做\(AC\)自动姬,我怀着逃避的心态做前面的字符串专题,然后,然后,这\(nm\)还是\(AC\)自动姬!(好吧其实这题可以用\(trie\)直接做,可是我已经走火入魔了)

我们先建一颗\(trie\)树,那么树中的每个节点显然都满足前缀的最后一个点。

由于\(AC\)自动姬的性质,我们知道一个节点的\(fail\)必然是树上的\(depth\)离自己最近且字母相同的一个点。

比如有\(abc\)\(c\)两个串插入。\(a\)\(fail\)\(root\)\(b\)\(fail\)\(root\),第一个\(c\)\(fail\)就是第二个\(c\)。假设第三个串是\(ab\),那么第一个\(b\)\(fail\)就是第二个\(b\)了。符合上述结论。

我们将每个点与其\(depth\)相差为\(1\)\(fail\)连一条边,表示这两个点只能选一个(相差的正好是首字母)。然后大力\(DP\)即可。

证明一下为什么不会有环:每次都是\(depth\)相差\(1\),是不可能有环的。

又到了激动人心的上代码环节。

#include<queue>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
using namespace std;

int T, n;
queue <int> q;
char s[1000002];

struct Trie {
	int fail[1000002], ch[1000002][30], cnt;
	int newnode() {
		for(int i = 0; i < 26; ++ i) ch[cnt][i] = 0;
		++ cnt;
		return cnt - 1;
	}
	void clear() {
		cnt = tot = 0;
		newnode();
	}
	void insert(const int len) {
		int p = 0, depth = 0;
		for(int i = 0; i < len; ++ i) {
			int op = s[i] - 'a';
			++ depth;
			if(! ch[p][op]) ch[p][op] = newnode(), dep[ch[p][op]] = depth;
			p = ch[p][op];
		}
	}
	void getfail() {
		for(int i = 0; i < 26; ++ i) {
			if(! ch[0][i]) continue;
			fail[ch[0][i]] = 0;
			q.push(ch[0][i]);
		}
		while(! q.empty()) {
			int u = q.front(); q.pop();
			for(int i = 0; i < 26; ++ i) {
				if(ch[u][i]) {
					fail[ch[u][i]] = ch[fail[u]][i];
					q.push(ch[u][i]);
				}
				else ch[u][i] = ch[fail[u]][i];
			}
		}
	}
	int tot, dot[2000002], nxt[2000002], head[1000002], dep[1000002];
	long long dp[1000002][2];
	bool vis[1000002];
	void addEdge(const int u, const int v) {
		dot[++ tot] = v;
		nxt[tot] = head[u];
		head[u] = tot;
	}
	void build() {
		for(int i = 1; i < cnt; ++ i) vis[i] = head[i] = 0;
		for(int i = 1; i < cnt; ++ i) {
			int fa = fail[i];
			if(dep[fa] + 1 == dep[i] && fa) addEdge(i, fa), addEdge(fa, i);
		}
	}
	void DP(const int u, const int fa) {
		vis[u] = 1;
		dp[u][0] = 0; dp[u][1] = 1;
		for(int i = head[u]; i; i = nxt[i]) {
			int v = dot[i];
			if(v == fa) continue;
			DP(v, u);
			dp[u][0] += max(dp[v][0], dp[v][1]);
			dp[u][1] += dp[v][0];
		}
	}
	void solve() {
		getfail(); build();
		long long ans = 0;
		for(int i = 1; i < cnt; ++ i) {
			if(vis[i]) continue;
			DP(i, 0);
			ans += max(dp[i][0], dp[i][1]);
		}
		printf("%lld\n", ans);
	}
	void debug()
    {
        for(int i = 0;i < cnt;i++)
        {
            printf("id = %3d,fail = %3d,chi = [",i,fail[i]);
            for(int j = 0;j < 26;j++)
                printf("%2d",ch[i][j]);
            printf("]\n");
        }
    }
}ac;

int read() {
	int x = 0, f = 1; char s;
	while((s = getchar()) > '9' || s < '0') {
		if(s == '-') f = -1;
		if(s == EOF) exit(0);	
	}
	while(s <= '9' && s >= '0') {
		x = (x << 1) + (x << 3) + (s ^ 48);
		s = getchar();
	}
	return x * f;
}

int main() {
	T = read();
	while(T --) {
		n = read();
		ac.clear();
		for(int i = 1; i <= n; ++ i) scanf("%s", s), ac.insert(strlen(s));
		ac.solve();
	}
	return 0;
}
原文地址:https://www.cnblogs.com/AWhiteWall/p/12358073.html