• HDU2296 Ring [AC自动机+DP]


      题意是说给你若干个单词,每个单词有一定的分数。如果一个字符串包含了某个单词就会得到该单词的分数,并且可以重复计算。让你输出一个长度不大于N并且分数最大的字符串,分数相等时选最短的,长度相等时选字典序最小的。

      DP方程很容易想,就是最普通的自动机DP。麻烦的地方在于保证字典序,如果从上向下DP,要保存前缀,比较字典序每次要从开头开始比较,可以写出来但是比较麻烦。我是将模式串反过来建图的,这样等于从后向前生成字符串,分数相同时每次选择较小的字符就一定能生成字典序最小的字符串,但要注意当两字符相等时要向前比较直到可以分出字典序为止。

      代码交上去跑了15ms,看statistic已经排在第五了,就是内存稍微多了一点。。。于是。。猥琐的把int改成了short,然后就跑到rank1去了。。嘿嘿。。

    #include <stdio.h>
    #include <string.h>
    #define MAXN 1001
    int cas,n,m,sc[101];
    char s[101][11];
    int next[MAXN][26],fail[MAXN],flag[MAXN],pos;
    char nc[MAXN];
    int newnode(){
        for(int i=0;i<26;i++)next[pos][i]=0;
        fail[pos]=flag[pos]=0;
        return pos++;
    }
    void insert(char *s,int sc){
        int len=strlen(s),p=0;
        //将字符串反向插入
        for(int i=len-1;i>=0;i--){
            int k=s[i]-'a';
            int &x=next[p][k];
            p=x?x:x=newnode();
            //nc记录这个位置的字符
            nc[p]=s[i];
        }
        //flag记录走到这一点的分数
        flag[p]=sc;
    }
    int q[MAXN],front,rear;
    void makenext(){
        q[front=rear=0]=0,rear++;
        while(front<rear){
            int u=q[front++];
            for(int i=0;i<26;i++){
                int v=next[u][i];
                if(v==0)next[u][i]=next[fail[u]][i];
                else q[rear++]=v;
                if(u&&v){
                    fail[v]=next[fail[u]][i];
                    //注意更新节点的价值
                    flag[v]+=flag[fail[v]];
                }
            }
        }
    }
    int d[51][MAXN];
    int fath[51][MAXN];
    int cmpfath(int now,int u,int v){
        while(now>0){
            if(nc[u]!=nc[v])return nc[u]<nc[v];
            u=fath[now][u],v=fath[now][v];
            now--;
        }
        return 0;
    }
    void dp(){
        memset(d,-1,sizeof d);
        d[0][0]=0;
        for(int i=0;i<n;i++){
            for(int u=0;u<pos;u++){
                if(d[i][u]==-1)continue;
                for(int k=0;k<26;k++){
                    int v=next[u][k];
                    //如果从该节点走过来价值比当前价值更高
                    if(d[i+1][v]<d[i][u]+flag[v]){
                        fath[i+1][v]=u;
                        d[i+1][v]=d[i][u]+flag[v];
                    //价值相等时比较字典序,一直向前比较直到不等
                    }else if(d[i+1][v]==d[i][u]+flag[v]){
                        if(cmpfath(i,u,fath[i+1][v]))fath[i+1][v]=u;
                    }
                }
            }
        }
        //确定最终走到哪一格,价值优先,其次是长度
        int st=-1,ans=-1;
        for(int i=0;i<=n;i++){
            for(int u=0;u<pos;u++){
                if(d[i][u]<=0)continue;
                if(ans==-1||d[i][u]>d[st][ans]){
                    st=i,ans=u;
                }else if(i<=st&&d[i][u]==d[st][ans]){
                    if(cmpfath(i,u,ans))st=i,ans=u;
                }
            }
        }
        //因为字符串是反过来的,要从后向前输出答案
        if(ans==-1){
            printf("\n");
        }else{
            for(int now=st,p=ans;now>0;p=fath[now][p],now--)
                printf("%c",nc[p]);
            printf("\n");
        }
    }
    int main(){
        scanf("%d",&cas);
        while(cas--){
            scanf("%d%d",&n,&m);
            for(int i=0;i<m;i++)scanf("%s",s[i]);
            for(int i=0;i<m;i++)scanf("%d",&sc[i]);
            pos=0;newnode();
            for(int i=0;i<m;i++){
                insert(s[i],sc[i]);
            }
            makenext();
            dp();
        }
        return 0;
    }
  • 相关阅读:
    【leetcode】1230.Toss Strange Coins
    2018.12.25 SOW
    L203 词汇题
    L202
    L201
    L200
    2018
    2018.12.21 Cmos- RF
    L198
    L196 Hospital educations
  • 原文地址:https://www.cnblogs.com/swm8023/p/2627535.html
Copyright © 2020-2023  润新知