hdu2296 Ring(AC自动机上dp)

题意:

给定m个不同的单词,单词由小写字母组成,每个单词都有自身的价值。
现在你要设计一个字符串,字符串的长度不能超过n。
如果字符串里面包含了某个单词,就可以获得相应的价值。
价值可以重复计算。单词可以相互重叠。

输出构造的最大价值的字符串。
如果价值相同,就选择长度最短的,如果长度一样短,那么就选择字典序最小。

数据范围:n<=50,m<=100

解法:
建立ac自动机,
容易想到令d[i][j]表示走i步,当前在j节点上的最大价值,
因为要输出串的方案,还需要记录路径,
因为串长度不超过50,直接开string类的数组path[i][j]记录整个路径的串,比较的时候也是直接比较就行了.

在ac自动机上dp即可.

注意特判最大价值等于0的情况,这时候应该输出空串.
code:
#include<bits/stdc++.h>
using namespace std;
const int maxm=2e3+5;
char s[maxm][15];
int n,m;
struct AC{
    int a[maxm][26],fail[maxm],val[maxm],tot=0;
    void init(){
        for(int i=0;i<=tot;i++){
            memset(a[i],0,sizeof a[i]);
            val[i]=fail[i]=0;
        }
        tot=0;
    }
    void add(char *s,int x){
        int len=strlen(s);
        int node=0;
        for(int i=0;i<len;i++){
            int v=s[i]-'a';
            if(!a[node][v])a[node][v]=++tot;
            node=a[node][v];
        }
        val[node]+=x;
    }
    void build(){//构造fail
    	queue<int>q;
        for(int i=0;i<26;i++){
            if(a[0][i]){
                q.push(a[0][i]);
            }
        }
        while(!q.empty()){
            int x=q.front();
            q.pop();
            val[x]+=val[fail[x]];//可以重复叠加
            for(int i=0;i<26;i++){
                if(a[x][i]){
                    fail[a[x][i]]=a[fail[x]][i];
                    q.push(a[x][i]);
                }else{
                    a[x][i]=a[fail[x]][i];
                }
            }
        }
    }
    //
    int d[55][maxm];//d[i][j]表示走i步当前在j节点的最大价值.
    string path[55][maxm];
    void solve(){
        //init
        for(int i=0;i<=n;i++){
            for(int j=0;j<=tot;j++){
                d[i][j]=-1;
                path[i][j]="";
            }
        }
        //dp
        d[0][0]=0;
        for(int i=0;i<n;i++){
            for(int j=0;j<=tot;j++){
                if(d[i][j]==-1)continue;
                for(int k=0;k<26;k++){
                    int x=a[j][k];
                    int temp=d[i][j]+val[x];
                    string p=path[i][j]+(char)(k+'a');
                    if(temp>d[i+1][x]){
                        d[i+1][x]=temp;
                        path[i+1][x]=p;
                    }else if(temp==d[i+1][x]){
                        if(p<path[i+1][x]){
                            path[i+1][x]=p;
                        }
                    }
                }
            }
        }
        int ma=0;
        for(int j=0;j<=tot;j++){
            ma=max(ma,d[n][j]);
        }
        if(ma==0){//特判ma=0的情况
            cout<<endl;return ;
        }
        string ans;
        for(int i=0;i<=n;i++){
            for(int j=0;j<=tot;j++){
                if(d[i][j]==ma){
                    if(ans==""||ans>path[i][j]){
                        ans=path[i][j];
                    }
                }
            }
            if(ans!="")break;
        }
        cout<<ans<<endl;
    }
}ac;
signed main(){
    int T;scanf("%d",&T);
    while(T--){
        ac.init();
        scanf("%d%d",&n,&m);
        for(int i=1;i<=m;i++){
            scanf("%s",s[i]);
        }
        for(int i=1;i<=m;i++){
            int x;scanf("%d",&x);
            ac.add(s[i],x);
        }
        ac.build();
        //
        ac.solve();
    }
    return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值