[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; }