Kattis - nvwls (AC自动机last优化 + dp)

题意

给出一个字典,每个单词去掉元音字母 A、E、I、O、U 之后形成一个新字典。

先给出一个只有辅音组成的串,用原字典中的单词还原该串,若存在多种还原方式,输出还原后元音字母数量最多的那种,若依旧多种,则任意输出。

传送门

思路

ac自动机fail树上跑dp的一眼套路题。

总结一下遇到的坑:

  1. 多个单词去掉元音字母之后形成的新单词相同。
  2. 若原单词只有辅音字母。
  3. dp过程中未保证完全还原辅音串。
  4. 特殊样例将 跳fail的过程卡成了 \(n^2\) 。

解决办法:

  1. 在字典树的结尾节点保存编号时,保存最大值。
  2. 将初值从 $0 $ 改为 \(-1\) 。
  3. dp数组初值同样改为 \(-1\),\(dp[0] = 0\) ,dp只能由非\(-1\)的状态转移。
  4. 对fail数组进行 last优化last[u] = len[fail[u]]? fail[u]: last[fail[u]];

last优化

普通方法将建图+匹配的复杂度成功优化为了 ??(∑??+??) ,但是,匹配成功时的计数也是需要跳fail边的。然而,为了跳到一个结束节点,我们可能需要中途跳到很多没用的伪结束节点:

如果一个节点的fail指向一个结尾节点,那么这个点也成为一个(伪)结尾节点。在匹配时,如果遇到结尾节点,就进行相应的计数处理。

这里面就又有优化的余地了:对于不是真正结束节点的伪结束点,直接跳过它就好了。我们用一个last指针表示“在它顶上的fail边所指向的一串节点中,第一个真正的结束节点”。于是,每次计数处理时,我们不跳fail边,改为跳last边,省去了很多冗余操作。

获得last指针的方法也十分简单,就是在void build()中加一句话:

last[u] = len[fail[u]]? fail[u]: last[fail[u]];

Code

#include <bits/stdc++.h>

using namespace std;

const int maxn = 3e5+10;

char a[maxn], str[maxn], S[maxn], tmp[maxn];
int res[maxn], pos[maxn], L[maxn];
int n, pn;
int val[maxn];

int trie[maxn][26], fail[maxn], last[maxn];
int len[maxn], dep[maxn];
int e[maxn];
int que[maxn],h, t;
int tot;

inline void insert(string t, int id) {
    register int p = 0;
    for (register int c, i = 0; t[i]; ++i) {
        c = t[i]-'A';
        if(!trie[p][c]) {
            trie[p][c] = ++tot;
            dep[tot] = dep[p] + 1;
        }
        p = trie[p][c];
    }
    if(val[e[p]] <= val[id]) e[p] = id;
    len[p] = dep[p];
}

inline void build() {
    h = 1, t = 0;
    for (register int i = 0; i < 26; ++i) {
        if(trie[0][i])
            que[++t] = trie[0][i];
    }
    while(h <= t) {
        register int u = que[h++];
        for (register int i = 0; i < 26; ++i) {
            if(trie[u][i]) fail[trie[u][i]] = trie[fail[u]][i], que[++t] = trie[u][i];
            else trie[u][i] = trie[fail[u]][i];
        }
        last[u] = len[fail[u]]? fail[u]: last[fail[u]];
    }
}

int pre[maxn], dp[maxn], sta[maxn];

inline void count(char* str) {
    dp[0] = 0;
    register int p = 0, LL = 0;

    for (register int i = 1; str[i]; ++i, ++LL) {
        register int c = str[i]-'A';
        p = trie[p][c];
        dp[i] = -1;
        for (register int j = p; j; j = last[j]) {
            if(e[j] && dp[i-len[j]]!=-1 && dp[i] < dp[i-len[j]]+val[e[j]]) {
                dp[i] = dp[i-len[j]]+val[e[j]];
                sta[i] = e[j];
                pre[i] = i-len[j];
            }
        }
    }

    for (register int i = LL; i > 0; i = pre[i]) res[++pn] = sta[i];
    for (register int i = pn; i >= 1; --i) {
        for (register int j = pos[res[i]]; j < pos[res[i]] + L[res[i]]; ++j) putchar(S[j]);
        if(i==1) putchar('\n');
        else putchar(' ');
    }
}

int main() {
    val[0] = -1;
    scanf("%d", &n);
    register  int ll = 0;
    for (register int i = 1; i <= n; ++i) {
    scanf("%s", a);
        pos[i] = ll; L[i] = strlen(a);
        strcat(S+ll, a); ll += L[i];
        val[i] = 0;
        register int LLL = 0;
        for (register int j = 0; a[j]; ++j) {
            char ch = a[j];
            if(ch=='A'||ch=='E'||ch=='I'||ch=='O'||ch=='U') ++val[i];
            else tmp[LLL++] = a[j];
        }
        tmp[LLL] = '\0';
        insert(tmp, i);
    }
    build();
    scanf("%s" ,str+1);
    count(str);
    return 0;
}
/*
8
BAEI
BAEIOU
CAEIOU
BCAEIOU
BCDAEIOU
DAEIOU
BDAEIOU
CDAEIOU
BCD
*/

原文地址:https://www.cnblogs.com/acerkoo/p/11670791.html

时间: 2024-10-10 04:03:23

Kattis - nvwls (AC自动机last优化 + dp)的相关文章

hdu 4057 AC自动机+状态压缩dp

http://acm.hdu.edu.cn/showproblem.php?pid=4057 Problem Description Dr. X is a biologist, who likes rabbits very much and can do everything for them. 2012 is coming, and Dr. X wants to take some rabbits to Noah's Ark, or there are no rabbits any more.

poj 1699 Best Sequence(AC自动机+状压DP)

题目链接:poj 1699 Best Sequence 题目大意:给定N个DNA序列,问说最少多长的字符串包含所有序列. 解题思路:AC自动机+状压DP,先对字符串构造AC自动机,然后在dp[s][i]表示匹配了s,移动到节点i时候的最短步数. #include <cstdio> #include <cstring> #include <queue> #include <vector> #include <iostream> #include &

hdu 2825 aC自动机+状压dp

Wireless Password Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 5640    Accepted Submission(s): 1785 Problem Description Liyuan lives in a old apartment. One day, he suddenly found that there

【HDU3341】 Lost&#39;s revenge (AC自动机+状压DP)

Lost's revenge Time Limit: 5000MS Memory Limit: 65535KB 64bit IO Format: %I64d & %I64u Description Lost and AekdyCoin are friends. They always play "number game"(A boring game based on number theory) together. We all know that AekdyCoin is t

HDU 2825 Wireless Password (AC自动机 + 状态压缩DP)

题目链接:Wireless Password 解析:给 m 个单词构成的集合,统计所有长度为 n 的串中,包含至少 k 个单词的方案数. AC自动机 + 状态压缩DP. DP[i][j][k]:长度为i的字符串匹配到状态j且包含k个magic word的可能字符串个数. AC代码: #include <algorithm> #include <iostream> #include <cstdio> #include <queue> #include <

HDU 3341 Lost&#39;s revenge AC自动机+ 状态压缩DP

题意:这个题目和HDU2457有点类似,都是AC自动机上的状态dp,题意就是给你只含有'A','T','C','G',四个字符的子串和文本串,问你文本串如何排列才可以使得文本串中包含有更多的模式串 解题思路:我们知道了 有 num[0] 个 'A', num[1] 个 ‘T’, num[2] 个 ‘C’,num[3] 个‘G’, 我们的可以知道暴力的思路就是把所有的文本串都枚举出来然后一一匹配.我们膜拜了一下春哥以后,就可以有以下思路:  把一个串的信息压缩一下,把具有同样个数字符的串看成是同一

HDU 3341 Lost&#39;s revenge(AC自动机+状压DP)

Lost's revenge Time Limit: 15000/5000 MS (Java/Others)    Memory Limit: 65535/65535 K (Java/Others)Total Submission(s): 4548    Accepted Submission(s): 1274 Problem Description Lost and AekdyCoin are friends. They always play "number game"(A bor

POJ 3691 (AC自动机+状态压缩DP)

题目链接:  http://poj.org/problem?id=3691 题目大意:给定N的致病DNA片段以及一个最终DNA片段.问最终DNA片段最少修改多少个字符,使得不包含任一致病DNA. 解题思路: 首先说一下AC自动机在本题中的作用. ①字典树部分:负责判断当前0~i个字符组成的串是否包含致病DNA,这部分靠字典树上的cnt标记完成. ②匹配部分:主要依赖于匹配和失配转移关系的计算,这部分非常重要,用来构建不同字符间状态压缩的转移关系(代替反人类的位运算). 这也是必须使用AC自动机而

AC自动机 + 矩阵优化 --- [BJOI2017]魔法咒语

bzoj 4860   LOJ2180   洛谷P3175 [BJOI2017]魔法咒语 题目描述: Chandra 是一个魔法天才. 从一岁时接受火之教会洗礼之后,Chandra 就显示出对火元素无与伦比的亲和力,轻而易举地学会种种晦涩难解的法术. 这也多亏 Chandra 有着常人难以企及的语言天赋,让她能轻松流利地说出咒语中那些极其拗口的魔法词汇. 直到十四岁,开始学习威力强大的禁咒法术时,Chandra 才遇到了障碍. 根据火之魔法规则,禁咒的构成单位是 N 个基本词汇. 施法时只要凝聚