AC自动机+DP P3041 视频游戏的连击 题解

题意:

贝西在玩一款游戏,该游戏只有三个技能键 “A”“B”“C”可用,但这些键可用形成N种(1 <= N<= 20)特定的组合技。第i个组合技用一个长度为1到15的字符串S_i表示。
当贝西输入的一个字符序列和一个组合技匹配的时候,他将获得1分。特殊的,他输入的一个字符序列有可能同时和若干个组合技匹配,比如N=3时,3种组合技分别为"ABA", "CB", 和"ABACB",若贝西输入"ABACB",他将获得3分。
若贝西输入恰好K (1 <= K <= 1,000)个字符,他最多能获得多少分?

题意好像很浅显,就是告诉你一堆模式串,叫你求出限定长度的最大匹配

反向思维是很重要的,就像Poi2000 病毒这道题。
那我们不妨设想:如果已经求出了这个串,该如何匹配?
答案是一直跳son,并对这个节点跳一次fail算答案。

那么我们就可以把每个点的贡献算出来(跳fail能得到的答案),在AC自动机上DP。

设计状态\(f[i][j]\),表示长度为\(i\)的字符串在跳到\(j\)这个点时这个点以及前面(可能是环,注意顺序就好)的最大答案。
那么显然有

\(f[i][tree[j].son[s]]=max(f[i][tree[j].son[s]],ans[tree[j].son[s]]+f[i-1][j])\)

解释一下,对于某个节点进行拓展,可以更新它儿子的f值。 如果取这个儿子进行拓展,答案是自己之前长度为j-1的最大值加上这个儿子的贡献,否组不取,枚举下一个儿子。

边界:f[0][u]=0 长度为零,答案自然为0
满足无后效性的方法:计算某个节点的f值的时候,要用到f[i-1][j],i就从1~maxlen枚举
son的访问顺序倒无所谓,反正用到的son的f是上一轮计算过的,按插入顺序枚举就好。

代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
int read(){
    int x=0,pos=1;char ch=getchar();
    for(;!isdigit(ch);ch=getchar()) if(ch=='-') pos=0;
    for(;isdigit(ch);ch=getchar()) x=x*10+ch-'0';
    return pos?x:-x;
}
int n;char s[2001],m[1000051];int f[1000051],dp[1001][10001];int l[2001];
struct ac{
    int son[4],fail,end,fl,q[4];
}tree[1000001];
int tot=0;
void insert(int cnt){
    int now=0;int len=l[cnt];
    for(int i=0;i<len;i++){
        if(!tree[now].son[s[i]-'A'+1]){
            tree[now].son[s[i]-'A'+1]=++tot;
            tree[now].q[s[i]-'A'+1]=1;
            memset(tree[tot].son,0,sizeof(tree[tot].son));
            tree[tot].fail=0;
        }
        now=tree[now].son[s[i]-'A'+1];
    }
    f[now]++;
    return;
}
void bfs(){
    queue<int>q;
    for(int i=1;i<=3;i++){
        if(tree[0].son[i]) {
            tree[tree[0].son[i]].fail=0; 
            q.push(tree[0].son[i]);
        }
    }
    while(!q.empty()){
        int now=q.front();
        q.pop();
        for(int i=1;i<=3;i++){
            if(tree[now].son[i]){
                int nex=tree[now].son[i];
                tree[nex].fail=tree[tree[now].fail].son[i];
                tree[tree[tree[now].fail].son[i]].fl=nex;
                q.push(nex);
            }else tree[now].son[i]=tree[tree[now].fail].son[i];
        }
        f[now]+=f[tree[now].fail];
    }
}
int maxlen;
int DP(){
    for(int i=0;i<=maxlen;i++){
        for(int j=1;j<=tot;j++){
            dp[i][j]=-19260817;
        }
    }
    for(int i=1;i<=maxlen;i++){
        for(int j=0;j<=tot;j++){
            for(int s=1;s<=3;s++){
                dp[i][tree[j].son[s]]=max(dp[i][tree[j].son[s]],f[tree[j].son[s]]+dp[i-1][j]);
            }
        }
    }
    int mn=0;
    for(int i=0;i<=tot;i++){
        mn=max(mn,dp[maxlen][i]);
    }
    return mn;
}
int main(){
    n=read();scanf("%d",&maxlen);
    tot=0;
    for(int i=1;i<=n;i++){
        scanf("%s",s);
        int len=strlen(s);
        l[i]=len;
        insert(i);
    }
    bfs();
    printf("%d",DP());
    return 0;
} 
posted @ 2019-07-27 22:48  lcyfrog  阅读(124)  评论(0编辑  收藏  举报