uva 1399 - Puzzle(AC自动机)

题目链接:uva 1399 - Puzzle

题目大意:给定K和N,表示有K种不同的字符,N个禁止串,求一个最长的串使得该串不包含任何禁止串为子串。如果存在循环或者不能构成的话,输出No。

解题思路:建立AC自动机,然后在AC自动机上做dp,所有单词结尾节点为禁止点。

#include <cstdio>
#include <cstring>
#include <queue>
#include <algorithm>

using namespace std;
const int maxn = 50005;
const int sigma_size = 26;

struct Aho_Corasick {
    int sz, ac[maxn][sigma_size];
    int fail[maxn], last[maxn];

    void init();
    int idx(char ch);
    void insert(char* s);
    void get_fail();
}fuck;

int K, vis[maxn], dp[maxn], jump[maxn];

int dfs (int u) {
    if (vis[u])
        return -1;

    if (dp[u] != -1)
        return dp[u];

    vis[u] = 1;
    int& ret = dp[u];
    jump[u] = -1;
    ret = 0;

    for (int i = K - 1; i >= 0; i--) {
        int v = fuck.ac[u][i];
        if (fuck.last[v] == 0) {
            int tmp = dfs(v);

            if (tmp == -1)
                return -1;

            if (tmp + 1 > ret) {
                ret = tmp + 1;
                jump[u] = i;
            }
        }
    }
    vis[u] = 0;
    return ret;
}

void put_ans (int u) {
    while (jump[u] != -1) {
        printf("%c", ‘A‘ + jump[u]);
        u = fuck.ac[u][jump[u]];
    }
    printf("\n");
}

int main () {
    int cas, n;
    char word[55];

    scanf("%d", &cas);
    while (cas--) {
        fuck.init();
        memset(vis, 0, sizeof(vis));
        memset(dp, -1, sizeof(dp));

        scanf("%d%d", &K, &n);
        for (int i = 0; i < n; i++) {
            scanf("%s", word);
            fuck.insert(word);
        }
        fuck.get_fail();

        if (dfs(0) != -1 && dp[0])
            put_ans(0);
        else
            printf("No\n");
    }
    return 0;
}

void Aho_Corasick::init() {
    sz = 1;
    memset(ac[0], 0, sizeof(ac[0]));
}

int Aho_Corasick::idx(char ch) {
    return ch - ‘A‘;
}

void Aho_Corasick::insert(char* s) {
    int n = strlen(s), u = 0;

    for (int i = 0; i < n; i++) {
        int v = idx(s[i]);

        if (ac[u][v] == 0) {
            memset(ac[sz], 0, sizeof(ac[sz]));
            last[sz] = 0;
            ac[u][v] = sz++;
        }
        u = ac[u][v];
    }
    last[u] = 1;
}

void Aho_Corasick::get_fail () {
    queue<int> que;
    fail[0] = 0;

    for (int i = 0; i < sigma_size; i++) {
        int u = ac[0][i];
        if (u) {
            fail[u] = 0;
            que.push(u);
        }
    }

    while (!que.empty()) {
        int r = que.front();
        que.pop();

        for (int i = 0; i < K; i++) {
            int u = ac[r][i];

            if (u == 0) {
                ac[r][i] = ac[fail[r]][i];
                continue;
            }

            que.push(u);
            int v = fail[r];

            while (v && ac[v][i] == 0)
                v = fail[v];

            fail[u] = ac[v][i];
            last[u] |= last[fail[u]];
        }
    }
}
时间: 2024-10-12 20:11:15

uva 1399 - Puzzle(AC自动机)的相关文章

UVA 1399 - Puzzle(AC自动机+DP)

UVA 1399 - Puzzle 题目链接 题意:给定一些字符串,求一个最长的不在包含这些子串的字符串,如果可以无限长输出No 思路:建ACM自动机,把不可走结点标记构造出来,然后在这个状态图上进行dp找出最长路径即可,至于无限长的情况,只要在dp前进行一次dfs判有没有环即可 代码: #include <cstdio> #include <cstring> #include <string> #include <algorithm> #include &

UVA 11468 - Substring(AC自动机)

UVA 11468 - Substring 题目链接 题意:给定一些模式串,然后给出一些字母出现的概率,每次随机出现一个字母,要求出这些字母出现L个组成的字符串不包含(即不是它的连续字串)已有模式串的概率 思路:AC自动机,先构造出AC自动机,构造的时候利用next数组的特性,记录下每个位置是否有经过一个单词结点,如果有这个结点就是不能走的结点,那么问题就变成了只能在能走的结点上走L步的概率,注意这里空边也要处理成可以走(走到它的next,因为不匹配的话就一直找到next能匹配的位置),然后进行

uva 11468 - Substring(AC自动机+概率)

题目链接:uva 11468 - Substring 题目大意:给出一些字符和各自字符对应的选择概率,随机选择L次后得到一个长度为L的字符串,要求该字符串不包含任意一个子串的概率. 解题思路:构造AC自动机之后,每随机生成一个字母,等于是在AC自动机上走一步,所有子串的结束位置的节点标记为禁止通行,然后问题转换成记忆搜索处理. #include <cstdio> #include <cstring> #include <queue> #include <algor

UVa 1399 Puzzle

方法:AC自动机 先把禁止的string插入ac自动机中,然后再这个自动机上求最长的合法路径.如果出现环或者最长路径长度为0,则输出"No":否则输出最长路径. code: 1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 #include <iostream> 5 #include <string> 6 #include <vector&

UVa 11468 Substring (AC自动机+概率DP)

题意:给出一个字母表以及每个字母出现的概率.再给出一些模板串S.从字母表中每次随机拿出一个字母,一共拿L次组成一个产度为L的串, 问这个串不包含S中任何一个串的概率为多少? 析:先构造一个AC自动机,然后随机生成L个字母,就是在AC自动机的某个结点走多少步,dp[i][j] 表示在 i 结点,并且剩下 j 步, 然后记忆化搜索就OK了. 代码如下: #pragma comment(linker, "/STACK:1024000000,1024000000") #include <

UVA 11468【AC自动机+DP】

dp[i][j]表示走了i步走到j结点的概率.初始值dp[0][0] = 1.当走到的结点不是单词尾结点时,才能走过去. !end[i]&&last[i] == root时,该结点才可行. 1 #include <bits/stdc++.h> 2 using namespace std; 3 const int N = 505; 4 int id(char c){ 5 if(c >= '0'&&c <= '9') return c-'0'; 6 if

AC自动机

AC自动机 直接学AC自动机比较难理解,强烈建议先学完KMP和字典树并进行一定的练习后,对于失配指针和字典树构造有一定理解后再来学AC自动机的内容.有关AC自动机的详细介绍可见刘汝佳的<算法竞赛入门经典训练指南>P214. 给你一个字典(包含n个不重复的单词),然后给你一串连续的字符串文本(长为len),问你该文本里面的哪些位置正好出现了字典中的某一个或某几个单词?输出这些位置以及出现的单词. 这个问题可以用n个单词的n次KMP算法来做(效率为O(n*len*单词平均长度)),也可以用1个字典

uva 1076 - Password Suspects(AC自动机+记忆化搜索)

题目链接:uva 1076 - Password Suspects 题目大意:有一个长度为n的密码,存在m个子串,问说有多少种字符串满足,如果满足个数不大于42,按照字典序输出. 解题思路:根据子串构建AC自动机,然后记忆化搜索,dp[i][u][s]表示第i个字符,在u节点,匹配s个子串. #include <cstdio> #include <cstring> #include <queue> #include <string> #include <

uva 1449 - Dominating Patterns(AC自动机)

题目练级:uva 1449 - Dominating Patterns 题目大意:有一个由小写字母组成的字符串集和一个文本T,要求找出那些字符串在文本中出现的次数最多. 解题思路:将字符串集建立AC自动机,然后传入T进行匹配,对每个匹配上的字符串多应次数加1,最后找出最大值.出现次数与最大值相同的字符串输出.注意字符集中出现相同字符的情况. #include <cstdio> #include <cstring> #include <queue> #include &l