洛谷 P3041 [USACO12JAN]Video Game G(AC自动机,dp)
传送门
解题思路
先对所有的string跑一遍AC自动机。
用sum表示到达一个节点的收益,可以在求fail时顺便继承着fail节点的sum值。
然后在这个AC自动机上做dp即可。
设dp[i][j]为第i步走到AC自动机上的j号节点的最大分数,枚举转移到的点数转移即可。
AC代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<vector>
#include<queue>
#include<map>
using namespace std;
const int inf=1e9;
const int maxn=1e3+5;
int n,m,tr[305][5],ans,dp[maxn][305],tot,num[305],sum[305],fail[305];
string s[30];
void insert(string s){
int now=0,len=s.length();
for(int i=0;i<len;i++){
int k=s[i]-'A';
if(tr[now][k]) now=tr[now][k];
else tr[now][k]=++tot,now=tot;
}
sum[now]++;
}
void build(){
queue<int> q;
for(int i=0;i<3;i++){
if(tr[0][i]) q.push(tr[0][i]);
}
while(!q.empty()){
int now=q.front();q.pop();
for(int i=0;i<3;i++){
if(tr[now][i]) fail[tr[now][i]]=tr[fail[now]][i],q.push(tr[now][i]);
else tr[now][i]=tr[fail[now]][i];
}
sum[now]+=sum[fail[now]];
}
}
int main(){
//freopen(".in","r",stdin);
//freopen(".out","w",stdout);
ios::sync_with_stdio(false);
cin>>n>>m;
for(int i=1;i<=n;i++) cin>>s[i],insert(s[i]);
build();
for(int i=0;i<=m;i++){
for(int j=1;j<=tot;j++){
dp[i][j]=-inf;
}
}
for(int i=0;i<m;i++){
for(int j=0;j<=tot;j++){
for(int k=0;k<3;k++){
dp[i+1][tr[j][k]]=max(dp[i+1][tr[j][k]],dp[i][j]+sum[tr[j][k]]);
}
}
}
for(int i=0;i<=tot;i++){
ans=max(ans,dp[m][i]);
}
cout<<ans;
return 0;
}