[USACO12JAN][AC自动机][动态规划] Video Game G
题面
令 \(f_{i,\ j}\) 表示以 \(Trie\) 树上 \(j\) 节点为末尾的长度为 \(i\) 的串的最大得分
\(f_{i+1,\ j} = max(f_{i,\ k}), k \in \{x\ |\ x 为 Trie 树上可转移过来的节点\}\)
# include <iostream>
# include <cstdio>
# include <queue>
# include <string>
# define MAXK 1005
# define MAXL 20005
int f[MAXK][MAXL];
bool vis[MAXK][MAXL];
int trie[MAXL][26], isEnd[MAXL];
int cntT, fail[MAXL];
void Insert(std::string s){
int now = 0, len = s.length();
for(int i = 0, ch; i < len; i++){
ch = s[i] - 'A';
if(!trie[now][ch]){
trie[now][ch] = ++cntT;
}
now = trie[now][ch];
}
isEnd[now]++;
}
void InitFail(){
std::queue<int>Q;
for(int i = 0; i < 26; i++){
if(trie[0][i]){
fail[trie[0][i]] = 0;
Q.push(trie[0][i]);
}
}
while(!Q.empty()){
int now = Q.front(); Q.pop();
for(int i = 0; i < 26; i++){
if(trie[now][i]){
fail[trie[now][i]] = trie[fail[now]][i];
Q.push(trie[now][i]);
}
else{
trie[now][i] = trie[fail[now]][i];
}
}
}
}
int Calc(int now, int val){ // 更新答案
while(now){
val += isEnd[now];
now = fail[now];
}
return val;
}
int main(){
int n, k;
std::string s;
std::cin>>n>>k;
for(int i = 1; i <= n; i++){
std::cin>>s;
Insert(s);
}
InitFail();
vis[0][0] = 1;
for(int i = 0; i < k; i++){ // 遍历每个长度
for(int j = 0; j <= cntT; j++){ // 遍历每个节点
if(!vis[i][j]){ // 只能用更新过的点更新
continue;
}
for(int now = 0, tmp; now < 3; now++){
tmp = trie[j][now];
f[i+1][tmp] = std::max(f[i+1][tmp], Calc(tmp, f[i][j]));
vis[i+1][tmp] = 1;
}
}
}
int ans = 0;
for(int i = 0; i <= cntT; i++){
ans = std::max(ans, f[k][i]);
}
std::cout<<ans;
return 0;
}