[USACO12JAN]视频游戏的连击Video Game Combos(AC自动机+DP)

Description

贝西正在打格斗游戏。游戏里只有三个按键,分别是“A”、“B”和“C”。游戏中有 N 种连击 模式,第 i 种连击模式以字符串 Si 表示,只要贝西的按键中出现了这个字符串,就算触发了一次连 击模式。不 同的连击模式是独立计算的,如果几个连击模式同时出现在贝西的按键顺序里,就算有重 叠部分, 也可以同时算作触发了多个模式。

假如有三个连击模式,分别是“AB”,“BA”,“ABC”,而贝西按下了“ABABC”,那么她一共 触发了四次 连击。假设贝西一共可以按 K 次键,那么她最多能触发多少次连击呢?

Solution

将字符串建立AC自动机

用dp[i][j]表示长度为i当前位置在自动机上j号节点上的连击数

mark[i]表示自动机上i号节点包括它的fail树上可获得的连击数

那么dp[i][T[j][k]]=max{dp[i-1][j]+mark[T[i][k]]}

Code

#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;

int n,m,T[520][4],fail[520],mark[520],tot=1,q[520],f[1010][520],Ans;
char s[20];

void Insert(){
	scanf("%s",s);
	int now=1,len=strlen(s);
	for(int i=0;i<len;++i){
		if(!T[now][s[i]-64]) T[now][s[i]-64]=++tot;
		now=T[now][s[i]-64];
	}
	mark[now]++;
}

void getfail(){
	for(int i=1;i<=3;++i) T[0][i]=1;
	int k,now,h=0,t=0;q[++t]=1;
	while(h<t){
		now=q[++h];
		for(int i=1;i<=3;++i)
			if(T[now][i]){
				k=fail[now];
				while(!T[k][i]) k=fail[k];
				fail[q[++t]=T[now][i]]=T[k][i];
			}else T[now][i]=T[fail[now]][i];
		mark[now]+=mark[fail[now]];
	}		
}

int main(){
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;++i) Insert();
	getfail();
	memset(f,128,sizeof(f));
	f[0][1]=0;
	for(int i=1;i<=m;++i)
		for(int j=1;j<=tot;++j)
			for(int k=1;k<=3;++k)
				f[i][T[j][k]]=max(f[i][T[j][k]],f[i-1][j]+mark[T[j][k]]);
	for(int i=1;i<=tot;++i) Ans=max(Ans,f[m][i]);
	printf("%d\n",Ans);
	return 0;
}

猜你喜欢

转载自www.cnblogs.com/void-f/p/8969097.html