[AC自动机+状压dp] hdu 4534 郑厂长系列故事——新闻净化

题意:中文的题目,意思就是说有很多串,每个串都有权值,权值为999的串必须出现,-999的串必须不出现。权值在-999~999之间。然后必须出现的串不超过8个。然后给一个全为小写目标串,问最少需要删除多少个字母才能够保证必须出现的串都出现,次数一样保证权值最大。输出次数和权值。然后根据样例,那些必须出现的串,其实权值是0。思路:很明显一开始建自动机构成trie图,但是需要注
摘要由CSDN通过智能技术生成

题意:中文的题目,意思就是说有很多串,每个串都有权值,权值为999的串必须出现,-999的串必须不出现。权值在-999~999之间。

然后必须出现的串不超过8个。然后给一个全为小写目标串,问最少需要删除多少个字母才能够保证必须出现的串都出现,次数一样保证权值最大。输出次数和权值。

然后根据样例,那些必须出现的串,其实权值是0。


思路:

很明显一开始建自动机构成trie图,但是需要注意的就是mark和sum的更新。个人是把所有中间的节点的sum全部赋值成了-inf。

接着只有8个必须出现的串,所以必须要状压一下。

所以最后就是dp[i][j][k][l],处理了i个字母,在j这个节点,k这个状态。l=0代表这个字母不删,l=1代表这个字母删。

然后是个结构体存两个值次数和评分。然后就是更新的时候有点麻烦。

然后注意一下,最后的评分有可能是负数。


#include"cstdlib"
#include"cstdio"
#include"cstring"
#include"cmath"
#include"queue"
#include"algorithm"
#include"iostream"
#include"map"
#include"string"
#define inf 999999
using namespace std;
struct trie
{
    int mark,id,sum;
    trie *next[27],*fail;
    trie()
    {
        mark=0;
        sum=-inf;
        memset(next,0,sizeof(next));
        fail=NULL;
    }
};
trie *root,*node[1700];
int triecont;
void init(char *v,int k,int x)
{
    trie *p=root;
    for(int i=0; v[i]; i++)
    {
        int tep=v[i]-'a';
        if(p->next[tep]==NULL)
        {
            p->next[tep]=new trie();
            p->next[tep]->id=triecont;
            node[triecont++]=p->next[tep];
        }
        p=p->next[tep];
    }
    if(k==1)    //必须出现
    {
        p->mark=(1<<x);
        p->sum=0;
    }
    else if(k==-1) p->mark=-1; //必须不出现
    else p->sum=x;            //无特定要求
}
void getac()
{
    queue<trie*>q;
    q.push(root);
    while(!q.empty())
    {
        trie *p=q.front();
        q.pop();
        for(int i=0; i<26; i++)
        {
            if(p->next[i]==NULL)
            {
                if(p==root) p->next[i]=root;
                else p->next[i]=p->fail->next[i];
            }
            else
            {
                if(p==root) p->next[i]->fail=root;
                else p->next[i]->fail=p->fail->next[i];
                q.push(p->next[i]);
                if(p!=root)
                {
                    if(p->next[i]->mark==-1 || p->next[i]->fail->mark==-1)   //更新必须不出现
                        p->next[i]->mark=-1;
                    else
                    {
                        p->next[i]->mark|=p->next[i]->fail->mark;
                        if(p->next[i]->fail->sum!=-inf)    //更新权值
                        {
                            if(p->next[i]->sum==-inf) p->next[i]->sum=0;
                            p->next[i]->sum+=p->next[i]->fail->sum;
                        }
                    }
                }
            }
        }
    }
}
struct Dp
{
    int cs,pf;
} dp[2][1605][259][2];   //这里必须要滚动,不然MLE
int main()
{
    int t,cas=1;
    cin>>t;
    while(t--)
    {
        triecont=0;
        root=new trie();
        node[triecont]=root;
        root->id=triecont++;
        int n,cnt=0;
        scanf("%d",&n);
        while(n--)
        {
            char x[177];
            int y;
            scanf("%s%d",x,&y);
            if(y==-999) init(x,-1,0);
            else if(y==999) init(x,1,cnt++);
            else init(x,0,y);
        }
        getac();
        char fuck[123];
        scanf("%s",fuck);
        int len=strlen(fuck);
        for(int i=0; i<triecont; i++)
            for(int j=0; j<(1<<cnt); j++)
                for(int k=0; k<2; k++)
                {
                    dp[0][i][j][k].cs=inf;
                    dp[0][i][j][k].pf=-inf;
                }
        dp[0][0][0][0].cs=dp[0][0][0][1].cs=0;
        dp[0][0][0][0].pf=dp[0][0][0][1].pf=0;
        for(int i=1; i<=len; i++)
        {
            for(int j=0; j<triecont; j++)
                for(int k=0; k<(1<<cnt); k++)
                    for(int l=0; l<2; l++)
                    {
                        dp[i%2][j][k][l].cs=inf;
                        dp[i%2][j][k][l].pf=-inf;
                    }
            for(int j=0; j<triecont; j++)
            {
                for(int k=0; k<(1<<cnt); k++)
                {
                    if(dp[1-i%2][j][k][0].cs==dp[1-i%2][j][k][1].cs && dp[1-i%2][j][k][0].cs==inf) continue;
                    for(int l=0; l<2; l++)
                    {
                        if(node[j]->next[fuck[i-1]-'a']->mark!=-1)  //这里需要注意,有的情况是必须删除的,所以不删除的情况是要判断一下的。
                        {
                            trie *p=node[j]->next[fuck[i-1]-'a'];
                            int tep=p->mark|k;
                            Dp cur=dp[i%2][p->id][tep][0],now;
                            if(dp[1-i%2][j][k][0].cs<dp[1-i%2][j][k][1].cs) now=dp[1-i%2][j][k][0];
                            else if(dp[1-i%2][j][k][0].cs>dp[1-i%2][j][k][1].cs) now=dp[1-i%2][j][k][1];
                            else
                            {
                                now.cs=dp[1-i%2][j][k][1].cs;
                                now.pf=max(dp[1-i%2][j][k][0].pf,dp[1-i%2][j][k][1].pf);
                            }
                            if(p->sum!=-inf)
                            {
                                if(now.pf==inf) now.pf=0;
                                now.pf+=p->sum;
                            }
                            if(now.cs<cur.cs) cur=now;
                            else if(now.cs==cur.cs) cur.pf=max(now.pf,cur.pf);
                            dp[i%2][p->id][tep][0]=cur;
                        }
                        Dp cur=dp[i%2][j][k][1],now;  //这里是这个位置删除的更新
                        if(dp[1-i%2][j][k][0].cs<dp[1-i%2][j][k][1].cs) now=dp[1-i%2][j][k][0];
                        else if(dp[1-i%2][j][k][0].cs>dp[1-i%2][j][k][1].cs) now=dp[1-i%2][j][k][1];
                        else
                        {
                            now.cs=dp[1-i%2][j][k][1].cs;
                            now.pf=max(dp[1-i%2][j][k][0].pf,dp[1-i%2][j][k][1].pf);
                        }
                        now.cs++;
                        if(now.cs<cur.cs) cur=now;
                        else if(now.cs==cur.cs) cur.pf=max(now.pf,cur.pf);
                        dp[i%2][j][k][1]=cur;
                    }
                }
            }
        }
        Dp ans;
        ans.cs=inf;
        ans.pf=-inf;
        for(int i=0; i<triecont; i++)
        {
            Dp now;
            if(dp[len%2][i][(1<<cnt)-1][0].cs<dp[len%2][i][(1<<cnt)-1][1].cs) now=dp[len%2][i][(1<<cnt)-1][0];
            else if(dp[len%2][i][(1<<cnt)-1][0].cs<dp[len%2][i][(1<<cnt)-1][1].cs) now=dp[len%2][i][(1<<cnt)-1][1];
            else
            {
                now.cs=dp[len%2][i][(1<<cnt)-1][1].cs;
                now.pf=max(dp[len%2][i][(1<<cnt)-1][0].pf,dp[len%2][i][(1<<cnt)-1][1].pf);
            }
            if(now.cs<ans.cs) ans=now;
            else if(now.cs==ans.cs) ans.pf=max(now.pf,ans.pf);
        }
        printf("Case %d: ",cas++);
        if(ans.cs==inf) puts("Banned");
        else printf("%d %d\n",ans.cs,ans.pf);
    }
    return 0;
}



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值