[USACO12JAN] Video Game Combo - AC自动机,dp

给定 \(N\) 个模式串。一个母串的分数定义为能与模式串匹配的次数,可以与同一个模式串多次匹配。问一个长度为 \(K\) 的母串最多能获得多少分。\(N\leq 20,K \leq 1000\)

Solution

考虑在 AC 自动机上 DP,令 \(f[i][j]\) 表示走了 \(i\) 个字符,到达结点 \(j\),则
\[ f[i][j]+val[ch[j][k]] \to f[i+1][ch[j][k]] \]
其中 \(val[p]\) 表示结点 \(p\) 到 fail 树根的链上有多少个末端

#include <bits/stdc++.h>
using namespace std;
int val[1005],ch[1005][3],fi[1005],dp[1005][1005],ind,n,m;
char str[1005];
void insert(char *s){
    int len=strlen(s),p=0;
    for(int i=0;i<len;i++){
        int v=s[i]-'A';
        if(!ch[p][v]) ch[p][v]=++ind;
        p=ch[p][v];
    }
    val[p]++;
}
void build(){
    queue <int> q;
    for(int i=0;i<3;i++)
        if(ch[0][i])
            q.push(ch[0][i]);
    while(!q.empty()){
        int p=q.front(); q.pop();
        for(int i=0;i<3;i++)
            if(ch[p][i]) fi[ch[p][i]]=ch[fi[p]][i], q.push(ch[p][i]);
            else ch[p][i]=ch[fi[p]][i];
        val[p]+=val[fi[p]];
    }
}
int main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++) scanf("%s",&str), insert(str);
    build();
    memset(dp,0x80,sizeof dp);
    for(int i=0;i<=m;i++) dp[i][0]=0;
    for(int i=1;i<=m;i++){
        for(int j=0;j<=ind;j++){
            for(int k=0;k<3;k++){
                dp[i][ch[j][k]]=max(dp[i][ch[j][k]],dp[i-1][j]+val[ch[j][k]]);
            }
        }
    }
    int ans=0;
    for(int i=0;i<=ind;i++)
        ans=max(ans,dp[m][i]);
    printf("%d\n",ans);
}

猜你喜欢

转载自www.cnblogs.com/mollnn/p/12450070.html