zoukankan      html  css  js  c++  java
  • HDU 2296 Ring ( Trie图 && DP && DP状态记录)

    题意 : 给出 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;
    const int Max_Tot = 1200;
    const int Letter = 26;
    
    int dp[55][1200];
    char s[55][1200][55];///存储每一个状态所代表的具体字符串
    
    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[0].Next, 0, sizeof(Node[0].Next));
            Node[0].fail = Node[0].val = 0;
            Size = 1;
        }
    
        inline void insert(char *s, int val){
            int now = 0;
            for(int i=0; s[i]; i++){
                int idx = s[i] - 'a';
                if(!Node[now].Next[idx]){
                    memset(Node[Size].Next, 0, sizeof(Node[Size].Next));
                    Node[Size].fail = Node[Size].val = 0;
                    Node[now].Next[idx] = Size++;
                }
                now = Node[now].Next[idx];
            }
            Node[now].val = val;
        }
    
        inline void BuildFail(){
            Node[0].fail = 0;
            for(int i=0; i<Letter; i++){
                if(Node[0].Next[i]){
                    Node[Node[0].Next[i]].fail = 0;
                    que.push(Node[0].Next[i]);
                }else Node[0].Next[i] = 0;
            }
            while(!que.empty()){
                int top = que.front(); que.pop();
                Node[top].val += Node[Node[top].fail].val;///这里需要注意!
                for(int i=0; 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;
    char tmp[111][55];
    int main(void)
    {
        int nCase;
        scanf("%d", &nCase);
        while(nCase--){
            int n, m;
            scanf("%d %d", &n, &m);
            for(int i=0; i<m; i++)
                scanf("%s", tmp[i]);
            int tmpVal;
            ac.init();
            for(int i=0; i<m; i++){
                scanf("%d", &tmpVal);
                ac.insert(tmp[i], tmpVal);
            }
            ac.BuildFail();
    
            for(int i=0; i<=n; i++){///将所有DP的值赋为 -1
                for(int j=0; j<ac.Size; j++){
                    dp[i][j] = -1;
                    s[i][j][0] = '';
                }
            }
    
            dp[0][0] = 0;///定义初始状态
    
            char str[60];
            int ii, jj, MaxSum;
            ii = jj = MaxSum = 0;
            for(int i=0; i<n; i++){
                for(int j=0; j<ac.Size; j++){
                    if(dp[i][j] >= 0){///如果当前dp值不是初始状态则进入if,否则其dp值毫无意义,直接跳过
                        for(int k=25; k>=0; k--){///一开始我是想谋求字典序最小而从后往前,但是WA一发后我发现我错了,实际上顺序不重要
                            int newi = i+1;
                            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+1] = '';
                            }else if(sum == dp[newi][newj]){///谋求字典序最小应该实在dp值相等情况下
                                strcpy(str, s[i][j]);
                                int len = strlen(str);
                                str[len] = 'a'+k;
                                str[len+1] = '';
                                if(strcmp(str, s[newi][newj]) < 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]);
                                    if(L1<=L2 && strcmp(s[newi][newj], s[ii][jj])<0)
                                        ii = newi, jj = newj;
                                }else{
                                    MaxSum = dp[newi][newj];
                                    ii = newi, jj = newj;
                                }
                            }
    
                        }
                    }
                }
            }
    
            if(MaxSum <= 0) puts("");///如果最后权值依旧是 0 那么输出空串
            else puts(s[ii][jj]);
        }
        return 0;
    }
  • 相关阅读:
    LeetCode 1275. 找出井字棋的获胜者 Find Winner on a Tic Tac Toe Game
    LeetCode 307. 区域和检索
    LeetCode 1271 十六进制魔术数字 Hexspeak
    秋实大哥与花 线段树模板
    AcWing 835. Trie字符串统计
    Leetcode 216. 组合总和 III
    Mybatis 示例之 复杂(complex)属性(property)
    Mybatis 示例之 复杂(complex)属性(property)
    Mybatis 高级结果映射 ResultMap Association Collection
    Mybatis 高级结果映射 ResultMap Association Collection
  • 原文地址:https://www.cnblogs.com/qwertiLH/p/7635861.html
Copyright © 2011-2022 走看看