UVA 11468 Substring (记忆化搜索 + AC自动鸡)

传送门

题意: 给你K个模式串, 然后,再给你 n 个字符, 和它们出现的概率 p[ i ], 模式串肯定由给定的字符组成。

   且所有字符,要么是数字,要么是大小写字母。 问你生成一个长度为L的串,不包含任何模式串的概率是多少。

解: 记忆化搜索 +  AC自动机。 对模式串建一个AC自动机, 不需要last[ ] 和 val[ ], 只需要一个 metch[ ]。

   维护一下这个点是否是某个模式串的最后一个字符节点,若是,则这个点不能走。

   然后, 剩下的就是从根节点,随便走 L 步, 记得要记忆化, 否则超时。

   记得数组开足够大, 否则 wa 一下午。

  

#include <bits/stdc++.h>
#define LL long long
#define rep(i, j, k) for(int i = j; i <= k; i++)
#define dep(i, j, k) for(int i = k; i >= j; i--)
#define INF 0x3f3f3f3f
#define inf 0x3f3f3f3f3f3f3f3f
#define mem(i, j) memset(i, j, sizeof(i))
#define pb push_back
using namespace std;

const int N = 20 * 20 + 5, M = 105;
char op[2];
double tmp;
struct Trie {
    int a[N][M], tot, metch[N], Fail[N], vis[N][M], n;
    double p[N], dp[N][M];
    char ch[M];
    void init() {
        mem(a[0], 0); tot = 1; metch[0] = 0; mem(dp, 0); mem(p, 0); mem(vis, 0);
    }
    int get(char Q) {
        if(Q >= ‘0‘ && Q <= ‘9‘) return Q - ‘0‘ + 1;
        if(Q >= ‘A‘ && Q <= ‘Z‘) return Q - ‘A‘ + 11;
        return Q - ‘a‘ + 37;
    }
    void join(char s[]) {
        int now = 0; int len = strlen(s);
        rep(i, 0, len - 1) {
            int id = get(s[i]);
            if(!a[now][id]) {
                mem(a[tot], 0); metch[tot] = 0;
                a[now][id] = tot++;
            }
            now = a[now][id];
        }
        metch[now] = 1;
    }
    void getFail() {
        queue<int> Q; while(!Q.empty()) Q.pop();
        rep(i, 1, M - 1) {
            if(a[0][i]) {
                Q.push(a[0][i]); Fail[a[0][i]] = 0;
            }
        }
        while(!Q.empty()) {
            int now = Q.front(); Q.pop();
            rep(i, 1, M - 1) {
                int u = a[now][i];
                if(u == 0) a[now][i] = a[Fail[now]][i];
                else {
                    Q.push(u);
                    Fail[u] = a[Fail[now]][i];
                    metch[u] |= metch[Fail[u]];
                }
            }
        }
    }
    double dfs(int now, int L) {
        if(!L) return 1.0;
        if(vis[now][L]) return dp[now][L];
        vis[now][L] = 1;
        double &ans = dp[now][L];
        ans = 0.0;
        rep(i, 1, M - 1) { /// 枚举所有可能字符
            if(!metch[a[now][i]]) { /// 这个节点可以走
                ans += p[i] * dfs(a[now][i], L - 1);
            }
        }
        return ans;
    }
    void scan() {
        scanf("%d", &n);
        rep(i, 1, n) {
            scanf("%s %lf", op, &tmp);
            int id = get(op[0]);
            p[id] = tmp;
        }
    }
    void print(int cas) {
        int L; scanf("%d", &L);
        printf("Case #%d: ", cas);
        printf("%.6f\n", dfs(0, L));
    }
};
Trie AC;
char b[25];
int main() {
    int _; scanf("%d", &_); int cas = 0;
    while(_--) {
        AC.init();
        int k; scanf("%d", &k);
        rep(i, 1, k) {
            scanf("%s", b); AC.join(b);
        }
        AC.getFail();
        AC.scan();
        AC.print(++cas);
    }
    return 0;
}

原文地址:https://www.cnblogs.com/Willems/p/12003847.html

时间: 2024-08-01 20:53:04

UVA 11468 Substring (记忆化搜索 + AC自动鸡)的相关文章

DP(记忆化搜索) + AC自动机 LA 4126 Password Suspects

题目传送门 题意:训练指南P250 分析:DFS记忆化搜索,范围或者说是图是已知的字串构成的自动机图,那么用 | (1 << i)表示包含第i个字串,如果长度为len,且st == (1 << m) - 1则是可能的.打印与之前相似. #include <bits/stdc++.h> using namespace std; typedef long long ll; const int N = 25 + 5; const int NODE = 10 * 10 + 5;

UVA 10604--Chemical Reaction+记忆化搜索

题目链接:点击进入 开始看到m和k都很小,就直接暴力了一发,结果T了.后面看到m只有6那么大,k又小于10,就觉得可以用记忆化搜索,状态我们就定为dp[n1][n2][n3][n4][n5][n6],表示n1–n6六种化学试剂的量,状态转移就时选两份试剂进行混合. 代码如下: #include<iostream> #include<cstring> #include<cstdio> using namespace std; #define INF 0x3f3f3f3f

UVA 10626--Buying Coke+记忆化搜索+DP

题目链接:点击进入 原来定义状态dp[n][n1][n5][n10]表示购买n瓶可乐后剩余1,5,10分硬币n1,n5,n10个时花费硬币数最小的数量.然后状态转移是:1.8个一分硬币购买第n瓶可乐,t=dp[n-1][n1+8][n5][n10]+8; 2.一个五分和3个1分,t=dp[n-1][n1+3][n5+1][n10]+4; 3.两个5分t=dp[n-1][n1][n5+2][n10]; 4.一个10分,t=dp[n-1][n5][n10+1]. 对于第一二种dp[n][n1][n5

uva 10118,记忆化搜索

这个题debug了长达3个小时,acm我不能放弃,我又回来了的第一题! 一开始思路正确,写法不行,结果越改越乱 看了网上某神的代码,学习了一下 /* * Author: Bingo * Created Time: 2015/3/2 21:23:20 * File Name: uva10118.cpp */ #include <iostream> #include <cstdio> #include <cstdlib> #include <cstring> #

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 11468 - Substring(AC自动机+概率)

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

UVA 11468 - Substring(AC自动机)

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

UVa 1252 - Twenty Questions(记忆化搜索,状态压缩dp)

题目链接:uva 1252 题意: 有n个长度为m的二进制串,每个都是不同的. 为了把所有字符串区分开,你可以询问,每次可以问某位上是0还是1. 问最少提问次数,可以把所有字符串区分开来. 思路来源于:点击打开链接 思路: m很小,可以考虑状态压缩. dp[s1][s2]表示询问的状态为s1时,此时能猜到状态包含s2时最小需要的步数. 当询问的几位=s2的二进制串小于2时就能区分出来了,dp[s1][s2]=0: 不能区分则再询问一次,s1|=(1<<k),如果问某位为0,则s2不变,问某位为

uva 10581 - Partitioning for fun and profit(记忆化搜索+数论)

题目链接:uva 10581 - Partitioning for fun and profit 题目大意:给定m,n,k,将m分解成n份,然后按照每份的个数排定字典序,并且划分时要求ai?1≤ai,然后输出字典序排在k位的划分方法. 解题思路:因为有ai?1≤ai的条件,所以先记忆化搜索处理出组合情况dp[i][j][s]表示第i位为j,并且剩余的未划分数为s的总数为dp[i][j][s],然后就是枚举每一位上的值,判断序列的位置即可. #include <cstdio> #include