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

时间:2023-03-10 04:14:43
[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;
}