这几天都在做\(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;
}