计蒜客-蒜场抽奖(AC自动机+状态压缩DP)

 

 

 

题解:题意不再说了,题目很清楚的。

思路:因为N<=10,所以考虑状态压缩 AC自动机中 val[1<<i]: 表示第i个字符串。AC自动机中fail指针是指当前后缀在其他串里面所能匹配的最长前缀的长度,然后我们在这里统计一下以该点结束所能包含的字符串的数量(就是在fail树中该点到根节点所经过的所有为单词结尾的点,在这里我们只要val[x] |= val[fail[x]]就行了,因为val[fail[x]]已经统计过 点fail[x]到根的值了)。

  考虑dp[i][j][k]:表示长度为i,第j个状态点,k为包含的单词的状态,  是否存在。然后转移方程为:if( ch[m-1][j][k] ) ch[m][ ch[j][ char ] ][ k|val[ ch[j][char] ] ]=1;(char : 为第j个状态再往后走一步到达的状态);

  我们的长度是一步一步走的,而且当前步数,仅有上一步确定,所以我们可以压缩步数为奇数偶数,变为:if( ch[ (m-1)&1 ][j][k] ) ch[ m&1 ][ ch[j][ char ] ][ k|val[ ch[j][char] ] ]=1;最后我们只要在第m步的每个状态包含不停字符串状态时的答案里面去最大值就行了。

参考代码:

#include<bits/stdc++.h>
using namespace std;
const int INF=0x3f3f3f3f;
const int maxn=1010;
int ch[maxn][4],val[maxn],fail[maxn];
int w[12],tot,n,m,Ans[maxn];
bool dp[2][maxn][1<<11];
char s[110];

void Init()
{
    tot=1;
    memset(val,0,sizeof val);
    memset(ch[tot],0,sizeof ch[tot]);    
}

void Insert(char *s,int x)
{
    int len=strlen(s),u=0;
    for(int i=0;i<len;++i)
    {
        int c=s[i]-'a';
        if(!ch[u][c]) {ch[u][c]=tot++;memset(ch[tot],0,sizeof ch[tot]);}
        u=ch[u][c];
    }
    val[u]=1<<x;
}

void GetFail()
{
    queue<int>q;
    for(int i=0;i<4;++i)
        if(ch[0][i]) q.push(ch[0][i]);
    while(!q.empty())
    {
        int u=q.front();q.pop();
        for(int i=0;i<4;++i)
        {
            int v=ch[u][i];
            if(!v){ch[u][i]=ch[fail[u]][i];continue;}
            q.push(v);
            fail[v]=ch[fail[u]][i];
            val[v]|=val[fail[v]];
        }
    }
}

void Work()
{
    dp[0][0][0]=1;
    for(int i=1;i<=m;++i)
    {
        memset(dp[i&1],0,sizeof dp[i&1]);
        for(int j=0;j<tot;++j)
            for(int k=0;k<4;++k)
                for(int z=0;z<(1<<n);++z)
                {
                    if(dp[(i-1)&1][j][z])
                        dp[i&1][ch[j][k]][z|val[ch[j][k]]]=1;    
                }
    }
}

int GetAns(int x)
{
    int ans=0;
    for(int i=0;i<n;++i)
        if(x&(1<<i)) ans+=w[i];
    return ans;    
}

int main()
{
    scanf("%d%d",&n,&m);
    Init();
    for(int i=0;i<n;++i)
        scanf("%s%d",s,w+i),Insert(s,i);
    GetFail();
    Work();
    int res=-INF;
    
    for(int j=0;j<(1<<n);++j) Ans[j]=GetAns(j);
    for(int i=0;i<tot;++i)
        for(int j=0;j<(1<<n);++j)
            if(dp[m&1][i][j]) res=max(res,Ans[j]);
    
    if(res<0) puts("Unhappy!");
    else printf("%d\n",res);
    
    return 0;    
}
View Code

 

posted @ 2019-09-04 15:10  StarHai  阅读(410)  评论(0编辑  收藏  举报