题意 : 给出 m 个单词,每一个单词有一个权重,如果一个字符串包含了这些单词,那么意味着这个字符串拥有了其权重,问你构成长度为 n 且权重最大的字符串是什么 ( 若有权重相同的,则输出最短且字典序最小的 )

分析 : 如果你做过 POJ 2778 或者 HDU 2243 以及诸如此类的题目,那么这道题的难点就不在构建 Trie图上了,没有接触过Trie图的建议先了解,下面进入正题。这道题相对于普通的 AC自动机orTrie图 + DP 的题目而言,共同点是都是利用 Trie图进行状态的转移,现在增加了权重以及要求输出具体的字符串答案。我们定义 DP[i][j] 为构建了长度为 i 且最后一个字符为 j 的字符串最大权重,由于每一个状态都对应一个字符串,所以再构建一个三维字符数组 s[i][j][k] 表示当前 i、j 状态下具体的字符串为 s[i][j][0~k-1],那么状态转移方程就是

DP[i+1][ Trie[j][k] ] = max( DP[i+1][ Trie[j][k] ] , DP[i][j] + Trie[j][k].val )

( Trie[j][k] 代表 j 状态可以一步转移到 k状态,如果你做过类似题目,那你不会陌生)

在状态转移的时候需要时时更新 s[i][j][k] 这个三维数组,当取得更优值的时候需要更新,最后只要在DP的过程当中记录最优的权重、状态i、j下标然后DP结束后输出即可。当然有个小优化,这种DP属于向前的DP,如果当前DP值是你设置的初值,那么它是没意义的,可以直接continue,因为它不会对后面的DP值产生影响。

#include<string.h>
#include<stdio.h>
#include<queue>
using namespace std;
;
;

][];
][][];///存储每一个状态所代表的具体字符串

struct Aho{
    struct StateTable{
        int Next[Letter];
        int fail, val;
    }Node[Max_Tot];
    int Size;
    queue<int> que;

    inline void init(){
        while(!que.empty()) que.pop();
        memset(Node[].Next, , ].Next));
        Node[].fail = Node[].val = ;
        Size = ;
    }

    inline void insert(char *s, int val){
        ;
        ; s[i]; i++){
            int idx = s[i] - 'a';
            if(!Node[now].Next[idx]){
                memset(Node[Size].Next, , sizeof(Node[Size].Next));
                Node[Size].fail = Node[Size].val = ;
                Node[now].Next[idx] = Size++;
            }
            now = Node[now].Next[idx];
        }
        Node[now].val = val;
    }

    inline void BuildFail(){
        Node[].fail = ;
        ; i<Letter; i++){
            ].Next[i]){
                Node[Node[].Next[i]].fail = ;
                que.push(Node[].Next[i]);
            }].Next[i] = ;
        }
        while(!que.empty()){
            int top = que.front(); que.pop();
            Node[top].val += Node[Node[top].fail].val;///这里需要注意!
            ; i<Letter; i++){
                int &v = Node[top].Next[i];
                if(v){
                    que.push(v);
                    Node[v].fail = Node[Node[top].fail].Next[i];
                }else v = Node[Node[top].fail].Next[i];
            }
        }
    }
}ac;
][];
int main(void)
{
    int nCase;
    scanf("%d", &nCase);
    while(nCase--){
        int n, m;
        scanf("%d %d", &n, &m);
        ; i<m; i++)
            scanf("%s", tmp[i]);
        int tmpVal;
        ac.init();
        ; i<m; i++){
            scanf("%d", &tmpVal);
            ac.insert(tmp[i], tmpVal);
        }
        ac.BuildFail();

        ; i<=n; i++){///将所有DP的值赋为 -1
            ; j<ac.Size; j++){
                dp[i][j] = -;
                s[i][j][] = '\0';
            }
        }

        dp[][] = ;///定义初始状态

        ];
        int ii, jj, MaxSum;
        ii = jj = MaxSum = ;
        ; i<n; i++){
            ; j<ac.Size; j++){
                ){///如果当前dp值不是初始状态则进入if,否则其dp值毫无意义,直接跳过
                    ; k>=; k--){///一开始我是想谋求字典序最小而从后往前,但是WA一发后我发现我错了,实际上顺序不重要
                        ;
                        int newj = ac.Node[j].Next[k];
                        int sum = dp[i][j] + ac.Node[ newj ].val;
                        if(sum > dp[newi][newj]){
                            dp[newi][newj] = sum;
                            strcpy(s[newi][newj], s[i][j]);
                            int len = strlen(s[i][j]);
                            s[newi][newj][len] = k+'a';
                            s[newi][newj][len+] = '\0';
                        }else if(sum == dp[newi][newj]){///谋求字典序最小应该实在dp值相等情况下
                            strcpy(str, s[i][j]);
                            int len = strlen(str);
                            str[len] = 'a'+k;
                            str[len+] = '\0';
                            )
                                strcpy(s[newi][newj], str);
                        }

                        if(dp[newi][newj] >= MaxSum){///更新一下最终的答案
                            if(dp[newi][newj] == MaxSum){
                                int L1 = strlen(s[newi][newj]);
                                int L2 = strlen(s[ii][jj]);
                                )
                                    ii = newi, jj = newj;
                            }else{
                                MaxSum = dp[newi][newj];
                                ii = newi, jj = newj;
                            }
                        }

                    }
                }
            }
        }

        ) puts("");///如果最后权值依旧是 0 那么输出空串
        else puts(s[ii][jj]);
    }
    ;
}

HDU 2296 Ring ( Trie图 && DP && DP状态记录)的更多相关文章

  1. HDU 2296 Ring (AC自动机+DP)

    Ring Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submis ...

  2. HDU 2296 Ring [AC自动机 DP 打印方案]

    Ring Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Submissio ...

  3. BZOJ3530: [Sdoi2014]数数(Trie图,数位Dp)

    Description 我们称一个正整数N是幸运数,当且仅当它的十进制表示中不包含数字串集合S中任意一个元素作为其子串.例如当S=(22,333,0233)时,233是幸运数,2333.20233.3 ...

  4. HDU 2296:Ring

    Problem Description For the hope of a forever love, Steven is planning to send a ring to Jane with a ...

  5. HDU 2296 Ring -----------AC自动机,其实我想说的是怎么快速打印字典序最小的路径

    大冥神的代码,以后能贴的机会估计就更少了....所以本着有就贴的好习惯,= =....直接贴 #include <bits/stdc++.h> using LL = long long ; ...

  6. HDU 4511 小明系列故事——女友的考验 ( Trie图 && DP )

    题意 :  给出编号从1 ~ n 的 n 个平面直角坐标系上的点,求从给出的第一个点出发到达最后一个点的最短路径,其中有两种限制,其一就是只能从编号小的点到达编号大的点,再者不能走接下来给出的 m 个 ...

  7. HDU 4057 Rescue the Rabbit ( AC自动机 + 状态压缩DP )

    模板来自notonlysuccess. 模式串只有10个,并且重复出现的分值不累加,因此很容易想到状态压缩. 将模式串加入AC自动机,最多有10*100个状态. dp[i][j][k]:串长为i,在T ...

  8. POJ 1625 Censored ( Trie图 && DP && 高精度 )

    题意 : 给出 n 个单词组成的字符集 以及 p 个非法串,问你用字符集里面的单词构造长度为 m 的单词的方案数有多少种? 分析 :先构造出 Trie 图方便进行状态转移,这与在 POJ 2278 中 ...

  9. HDU 3341 Lost's revenge ( Trie图 && 状压DP && 数量限制类型 )

    题意 : 给出 n 个模式串,最后给出一个主串,问你主串打乱重组的情况下,最多能够包含多少个模式串. 分析 : 如果你做过类似 Trie图 || AC自动机 + DP 类似的题目的话,那么这道题相对之 ...

随机推荐

  1. 【MM系列】SAP MM模块-BOM展开函数

    公众号:SAP Technical 本文作者:matinal 原文出处:http://www.cnblogs.com/SAPmatinal/ 原文链接:[MM系列]SAP MM模块-BOM展开函数   ...

  2. vim加脚本注释和文本加密

    vim /etc/vimrc 一.李导版本 autocmd BufNewFile *.py,*.cc,*.sh,*.java exec ":call SetTitle()" fun ...

  3. 精读《What's new in javascript》

    1. 引言 本周精读的内容是:Google I/O 19. 2019 年 Google I/O 介绍了一些激动人心的 JS 新特性,这些特性有些已经被主流浏览器实现,并支持 polyfill,有些还在 ...

  4. 使用原生js 获取用户访问项目的浏览器类型

    想要获取浏览器的类型很简单,网上提供了很多方法,但是看过之后,都是根据浏览器内核来判断是ie,谷歌,火狐,opeara的, 所以不能进一步判断在国内使用的主流浏览器类型,比如360,百度,搜狐浏览器等 ...

  5. pg_receivewal实践

    测试从pg_receivewal的日志中恢复从库为主库: 主从配置async模式,配置pg_receivewal接收日志pg_receivewal -D /dbaas/pg/data/pg_recei ...

  6. jmeter应用之批量插入数据

    上一篇讲到如何在jmeter中配置并连接使用mysql数据库,这一节主要是讲数据库连接的简单应用——批量插入数据 总体步骤如下: 1)新建线程组和添加JDBC Connection Configura ...

  7. 洛谷 P1631 序列合并(优先队列)

    传送门 解题思路 首先读入a.b数组后,sort一遍(从小到大),然后把a[1]+b[1],a[2]+b[1],a[3]+b[1]……a[n]+b[1]全部加入一个优先队列q(小根堆). 然后从一到n ...

  8. MySQL explain,type分析(转)

    问题:explain结果中的type字段代表什么意思? MySQL的官网解释非常简洁,只用了3个单词:连接类型(the join type).它描述了找到所需数据使用的扫描方式. 最为常见的扫描方式有 ...

  9. python3 pycurl 出现 TypeError: string argument expected, got 'bytes' 解决方案

    用pycurl请求指定链接并返回结果时出现 TypeError: string argument expected, got 'bytes'  错误 经过排查问题出现在使用StringIO的write ...

  10. P4556 [Vani有约会]雨天的尾巴(线段树合并+lca)

    P4556 [Vani有约会]雨天的尾巴 每个操作拆成4个进行树上差分,动态开点线段树维护每个点的操作. 离线处理完向上合并就好了 luogu倍增lca被卡了5分.....于是用rmq维护.... 常 ...