[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;
}
posted @ 2020-09-07 09:14  ChPu437  阅读(97)  评论(0编辑  收藏  举报