LGP3041 Video Game G (AC自动机+dp)
LGP3041 Video Game G
Mean
Bessie 在玩一款游戏,该游戏只有三个技能键 A,B,C 可用,但这些键可用形成 \(n\) 种特定的组合技。第 \(i\) 个组合技用一个字符串 \(s_i\)表示。
Bessie 会输入一个长度为 \(k\) 的字符串 \(t\),而一个组合技每在 \(t\) 中出现一次,Bessie 就会获得一分。\(s_i\)在 tt 中出现一次指的是 \(s_i\)是 \(t\) 从某个位置起的连续子串。如果 \(s_i\)从 \(t\) 的多个位置起都是连续子串,那么算作 \(s_i\)出现了多次。
若 Bessie 输入了恰好 \(k\) 个字符,则她最多能获得多少分?
Sol
AC自动机+dp
定义一个节点的权值\(cnt[x]\),代表\(x\)节点向上跳\(fail\)时一共能经过多少个终止节点.
定义一条路径的权值为该路径上所有点权的和.
问题转换成在状态机上走\(m\)步,所有长度为\(m\)的路径中,路径权值的最大值.
\(dp[i][j]\),代表当前走了\(i\)步,走到状态机上节点\(j\)时,长度为\(i\)的最大路径值.
则有 \(dp[i+1][tr[j][op]]=max(dp[i+1][tr[j][op]],dp[i][j]+(cnt[tr[j][op]]));\)
最后统计一下最大值即可.
Code
#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=(a);i<=(b);++i)
#define dep(i,a,b) for(int i=(a);i>=(b);--i)
#define lowbit(x) (x&(-x))
#define debug(x) cout<<#x<<" :"<<x<<endl
#define debug1(x) cout<<#x<<" :"<<x<<" "
using namespace std;
typedef unsigned long long ull;
typedef long long ll;
inline ll read() {
char c=getchar();ll x=0,f=1;
while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
while(c>='0'&&c<='9'){x=x*10+c-'0',c=getchar();}
return x*f;
}
inline void out(int x) {
if(x>9) out(x/10);
putchar(x%10+'0');
}
const int mod = 1e4+7;
const int N = 10000+10;
const int M = 200;
int tr[N][4],fail[N];
int vis[N];
/**
* AC自动机 + dp
* 求长度为m的任意串 中匹配串出现的最多次数
*/
char s[N];
int tot;
int n,k;
int dp[1005][500];
int cnt[N];
int qpow(int a,int b){
int ans=1;
while(b){
if(b&1){
ans=(1ll*ans*a)%mod;
}
a=(1ll*a*a)%mod;
b>>=1;
}
return ans;
}
void insert(int id){
int p=0;
for(int i=1;s[i];i++){
int v=s[i]-'A';
if(!tr[p][v]){
tr[p][v]=++tot;
}
p=tr[p][v];
}
vis[p]=1;
cnt[p]=1;
}
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 p = Q.front();
Q.pop();
for(int i=0;i<3;++i){
if(tr[p][i]){
fail[tr[p][i]]=tr[fail[p]][i];
Q.push(tr[p][i]);
cnt[tr[p][i]]+=cnt[fail[tr[p][i]]];
}
else{
tr[p][i]=tr[fail[p]][i];
}
}
}
}
int main(){
scanf("%d %d",&n,&k);
for(int i=1;i<=n;++i){
scanf("%s",s+1);
insert(i);
}
build();
for(int i=0;i<=k;++i){
for(int j=0;j<=tot;++j){
dp[i][j]=-0x3f3f3f3f;
}
}
dp[0][0]=0;
for(int i=0;i<k;++i){
for(int j=0;j<=tot;++j){
for(int op=0;op<3;++op){
dp[i+1][tr[j][op]]=max(dp[i+1][tr[j][op]],dp[i][j]+(cnt[tr[j][op]]));
}
}
}
int ans=0;
for(int i=0;i<=tot;++i){
ans=max(ans,dp[k][i]);
}
printf("%d\n",ans);
}