100道动态规划——29 UVALive 5766 GRE Words AC自动机上的动态规划

        之前怎么写都是WA,空了一天写还是WA,把cin和cout都换成scanf和printf就过,奇怪的问题

        定义状态dp[i]表示选择第i个串的最大价值

        状态转移方程就是 dp[i]=max{dp[j]}+value[i],其中要求0<=j<i,且串j是串i的子串

        因此问题就归结于高效判断子串的问题,这要求我们把每一个串都保存下来,当然,你记录每个点的father也行

        然后每次对于串i,就在AC自动机上扫它的子串

        代码里rec[i]表示第i个串的最后一个字符在AC自动机上的节点位置

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>

using namespace std;

const int maxm=3E5+5;

int times,ans,kcase,sz,root,n,ch[maxm][26],fail[maxm],last[maxm],dp[maxm],v[maxm],rec[20005],val[20005],pos[20005],newnode();
char str[maxm];
void getfail(),ins(int k),init();

int main(){
//    ios_base::sync_with_stdio(false);
    scanf("%d",×);
    while(times--){
        scanf("%d",&n);
        //cin>>n;
        init();
        for(int i=0;i<n;++i){
            scanf("%s%d",str+pos[i],&val[i]);
            pos[i+1]=pos[i]+strlen(str+pos[i]);
            //cin>>str[i];
            ins(i);
            //cin>>val[i];
        }
        getfail();

        for(int i=0,u=root,p;i<n;++i,u=root)
        if(val[i]>0){
            for(int j=pos[i];j<pos[i+1];++j){
                p=u=ch[u][str[j]];
                if(!v[p]&&last[p]!=root)
                    p=last[p];
                while(v[p]){
                    dp[rec[i]]=max(dp[rec[i]],dp[p]);
                    p=last[p];
                }
            }
            dp[rec[i]]+=val[i];
            ans=max(ans,dp[rec[i]]);
        }
//        for(int i=0;i<n;++i)
//            cout<<dp[rec[i]]<<' ';
        printf("Case #%d: %d\n",++kcase,ans);
//        cout<<"Case #"<<++kcase<<": "<<ans<<endl;
    }
    return 0;
}

void init(){
    sz=ans=0;
    root=newnode();
}

void ins(int k){
    int u=root;
    for(int i=pos[k];str[i];++i){
        str[i]-='a';
        if(ch[u][str[i]]==root)
            ch[u][str[i]]=newnode();
        u=ch[u][str[i]];
    }
    v[u]=1;
    rec[k]=u;
}

void getfail(){
    queue<int> q;
    for(int i=0;i<26;++i)
    if(ch[root][i]!=root){
        q.push(ch[root][i]);
        fail[ch[root][i]]=last[ch[root][i]]=root;
    }

    int u;
    while(!q.empty()){
        u=q.front();q.pop();
        for(int i=0;i<26;++i)
        if(ch[u][i]==root)
            ch[u][i]=ch[fail[u]][i];
        else{
            fail[ch[u][i]]=ch[fail[u]][i];
            last[ch[u][i]]=(v[fail[ch[u][i]]]?fail[ch[u][i]]:last[fail[ch[u][i]]]);
            q.push(ch[u][i]);
        }
    }
}

int newnode(){
    dp[sz]=v[sz]=0;
    memset(ch[sz],0,sizeof ch[sz]);
    return sz++;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值