[AC自动机+概率dp] hdu 3689 Infinite monkey theorem

题意:

给n个字母,和m次数。

然后输入n个字母出现的概率

然后再给一个目标串str

然后问m次中敲出目标串的概率是多少。

思路:

AC自动机+概率dp的简单题。

首先建立trie图,然后就是状态转移了

dp版本:

dp三重循环变量次数,节点数,和字母数

代码:

#include"cstdlib"
#include"cstdio"
#include"cstring"
#include"cmath"
#include"queue"
#include"algorithm"
#include"iostream"
#include"map"
#include"string"
using namespace std;
int triecont;
double dp[1234][22];
struct trie
{
    int mark,id;
    trie *next[27],*fail;
    trie()
    {
        mark=id=0;
        memset(next,0,sizeof(next));
        fail=NULL;
    }
};
trie *root,*node[22];
void init(char *v)
{
    trie *p=root;
    for(int i=0;v[i];i++)
    {
        int tep=v[i]-'a';
        if(p->next[tep]==NULL)
        {
            p->next[tep]=new trie();
            node[triecont]=p->next[tep];
            p->next[tep]->id=triecont++;
        }
        p=p->next[tep];
    }
    p->mark++;
}
void getac()
{
    queue<trie*>q;
    q.push(root);
    while(!q.empty())
    {
        trie *p=q.front();
        q.pop();
        for(int i=0;i<26;i++)
        {
            if(p->next[i]==NULL)
            {
                if(p==root) p->next[i]=root;
                else p->next[i]=p->fail->next[i];
            }
            else
            {
                if(p==root) p->next[i]->fail=root;
                else p->next[i]->fail=p->fail->next[i];
                q.push(p->next[i]);
            }
        }
    }
}
int main()
{
    int n,m;
    while(scanf("%d%d",&n,&m),(n+m))
    {
        double gl[27];
        memset(gl,0,sizeof(gl));
        memset(node,0,sizeof(node));
        while(n--)
        {
            char x[2];
            double y;
            scanf("%s%lf",x,&y);
            gl[x[0]-'a']=y;
        }
        char fuck[27];
        scanf("%s",fuck);
        triecont=0;
        root=new trie();
        node[triecont]=root;
        root->id=triecont++;
        init(fuck);
        getac();
        memset(dp,0,sizeof(dp));
        dp[0][0]=1;
        for(int i=1;i<=m;i++)
        {
            for(int j=0;j<triecont-1;j++)
            {
                for(int k=0;k<26;k++)
                {
                    trie *p=node[j]->next[k];
                    dp[i][p->id]+=dp[i-1][j]*gl[k];
                }
            }
        }
        double ans=0;
        for(int i=0;i<=m;i++) ans+=dp[i][triecont-1];

        printf("%.2f%%\n",ans*100);
    }
    return 0;
}

建立可达矩阵版本:

注意到达目标状态 那么他之后的状态的概率就都是1了

然后用快速幂加速~

#include"cstdlib"
#include"cstdio"
#include"cstring"
#include"cmath"
#include"queue"
#include"algorithm"
#include"iostream"
using namespace std;
int triecont;
struct trie
{
    int mark,id;
    trie *next[27];
    trie *fail;
    trie()
    {
        mark=id=0;
        memset(next,0,sizeof(next));
        fail=NULL;
    }
};
struct matrix
{
    double mat[20][20];
};
trie *root;
void init(char *v)
{
    trie *p=root;
    for(int i=0; v[i]; i++)
    {
        int tep=v[i]-'a';
        if(p->next[tep]==NULL)
        {
            p->next[tep]=new trie();
            p->next[tep]->id=triecont++;
        }
        p=p->next[tep];
    }
    p->mark=1;
}
void getac()
{
    queue<trie*>q;
    q.push(root);
    while(!q.empty())
    {
        trie *p;
        p=q.front();
        q.pop();
        for(int i=0; i<26; i++)
        {
            if(p->next[i]==NULL)
            {
                if(p==root) p->next[i]=root;
                else p->next[i]=p->fail->next[i];
            }
            else
            {
                if(p==root) p->next[i]->fail=root;
                else  p->next[i]->fail=p->fail->next[i];
                q.push(p->next[i]);
            }
        }
    }
}
matrix matmul(matrix a,matrix b,int n)
{
    int i,j,k;
    matrix c;
    memset(c.mat,0,sizeof(c.mat));
    for(i=0; i<n; i++)
    {
        for(j=0; j<n; j++)
        {
            for(k=0; k<n; k++)
            {
                c.mat[i][j]+=a.mat[i][k]*b.mat[k][j];
            }
        }
    }
    return c;
}
matrix matpow(matrix a,int k,int n)
{
    matrix b;
    int i;
    memset(b.mat,0,sizeof(b.mat));
    for(i=0; i<n; i++) b.mat[i][i]=1;
    while(k)
    {
        if(k&1) b=matmul(a,b,n);
        a=matmul(a,a,n);
        k>>=1;
    }
    return b;
}
int main()
{
    int n,m;
    while(scanf("%d%d",&n,&m),(n+m))
    {
        double gl[27];
        memset(gl,0,sizeof(gl));
        while(n--)
        {
            char x[2];
            double y;
            scanf("%s%lf",x,&y);
            gl[x[0]-'a']+=y;
        }
        triecont=0;
        root=new trie();
        root->id=triecont++;
        char x[12];
        scanf("%s",x);
        init(x);
        getac();
        queue<trie*>q;
        q.push(root);
        int used[12];
        memset(used,0,sizeof(used));
        matrix a,ans;
        memset(a.mat,0,sizeof(a.mat));
        while(!q.empty())
        {
            trie *p=q.front();
            q.pop();
            if(used[p->id]) continue;
            used[p->id]=1;
            if(p->mark==1)        //目标状态 后续状态都是本身
            {
                a.mat[p->id][p->id]=1;
                continue;
            }
            for(int i=0;i<26;i++)
            {
                if(used[p->next[i]->id]==0) q.push(p->next[i]);
                a.mat[p->id][p->next[i]->id]+=gl[i];
            }
        }
        /*for(int i=0;i<triecont;i++)
        {
            for(int j=0;j<triecont;j++) printf("%.2f ",a.mat[i][j]);
            puts("");
        }*/
        ans=matpow(a,m,triecont);
        printf("%.2f%%\n",ans.mat[0][triecont-1]*100);
    }
    return 0;
}
时间: 2024-10-27 07:20:16

[AC自动机+概率dp] hdu 3689 Infinite monkey theorem的相关文章

HDU 3689 Infinite monkey theorem [KMP DP]

Infinite monkey theorem Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 1702 Accepted Submission(s): 882 Problem Description Could you imaging a monkey writing computer programs? Surely monkeys are

[HDU 3689]Infinite monkey theorem (KMP+概率DP)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3689 黄老师说得对,题目只有做wa了才会有收获,才会有提高. 题意:一个猴子敲键盘,键盘上有n个键,猴子敲第i个键的概率是p[i],问敲m次后形成的字符串里出现给定串的概率是多少. 这实际上就跟那个ac自动机转为trie图然后dp一样的. 类似的题目有POJ 2778,这篇题解不错:http://blog.csdn.net/luyuncheng/article/details/8643001 只不过

●HDU 3689 Infinite monkey theorem

题链: http://acm.hdu.edu.cn/showproblem.php?pid=3689题解: KMP,概率dp (字符串都从1位置开始) 首先对模式串S建立next数组. 定义dp[i][j]表示猴子打的串长度为i,且该串的后缀与模式串最多匹配到j位置的概率. 显然dp[0][0]=1, 考虑如何转移: 枚举下一个打出的字符为c,然后用kmp的next数组找到模式串中可以继续匹配的位置k. 即:k=j+1; while(k&&S[k]!=c) k=next[k]; 然后将dp

HDU 3689 Infinite monkey theorem(KMP + DP)

题目链接:点击打开链接 思路: 用d[i][j]表示前i个字符,已经匹配了字母中的j个字符,最终包含这个字母的概率. 每次转移的时候有n个方向, 表示第i个字符选哪个字符, 那么有个问题, 如果我当前选的这个字符失配了, 那么转移之后我还匹配了多少个字符. 这恰恰是KMP能做的. 细节参见代码: #include <cstdio> #include <cstring> #include <algorithm> #include <iostream> #inc

hdu 3689 Infinite monkey theorem

TMD这些神奇的猴子... DP里面用KMP的next的数组来搞一搞,(不是很会,一开始想这样搞,然而思路很乱,就弃疗了,,,DP太虚了) 1 #include<bits/stdc++.h> 2 #define N 1000005 3 #define LL long long 4 #define inf 0x3f3f3f3f 5 using namespace std; 6 inline int ra() 7 { 8 int x=0,f=1; char ch=getchar(); 9 whil

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 <

bzoj1444 有趣的游戏(AC自动机+概率dp)

题意: 给定n个长度为l的模式串,现在要用前m个大写字母生成一个随机串,每个字符有自己的出现几率,第一次出现的字符串获胜,求最终每个字符串的获胜几率. 分析: 容易想到先把所有的字符串建成一个AC自动机 然后对于生成的随机串就相当于从AC自动机的root开始在自动机上走,然后求走到每个单词节点的概率 因为这是存在环的,不是DAG图,所以不能直接DP 考虑构造出刚开始的转移矩阵,然后对转移矩阵作矩阵乘法不断迭代就能得到正确答案了 转移矩阵如何建呢? 1)a[i][ch[i][j]]+=p[j] (

【BZOJ1444】[Jsoi2009]有趣的游戏 AC自动机+概率DP+矩阵乘法

[BZOJ1444][Jsoi2009]有趣的游戏 Description Input 注意 是0<=P Output Sample Input Sample Output HINT  30%的数据保证, n ≤ 2. 50%的数据保证, n ≤ 5. 100%的数据保证, n , l, m≤ 10. 题解:本题的做法真的很多啊,概率DP,期望DP,当然还有矩乘黑科技~ 就是先跑AC自动机,弄出转移矩阵,然后自乘50次就行了. #include <cstdio> #include <

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

将K个模板串构成一个AC自动机,那些能匹配到的单词节点都称之为禁止节点. 然后问题就变成了在Tire树上走L步且不经过禁止节点的概率. 根据全概率公式用记忆化搜索求解. 1 #include <cstdio> 2 #include <cstring> 3 #include <queue> 4 using namespace std; 5 6 const int maxnode = 500; 7 const int sigma_size = 64; 8 int idx[2