zoukankan      html  css  js  c++  java
  • HDU3689 Infinite monkey theorem 无限猴子(字符串DP+KMP)

    题目描述:

    大概的意思就是根据无限猴子定理,无限只猴子坐在打字机旁瞎敲,总有一个能敲出莎士比亚文集。现在给你一个打字机和一只猴子,打字机的每个按钮(共n个)上的字母及猴子按下这个按钮的概率已知,而且猴子只能按m下按钮,又给定一个串,问猴子打出的乱码中含这个串的概率。

    其中n<=26,m<=1000,多组数据,以n=0,m=0结束。以百分数形式输出,保留小数点后2位。

    样例:

    输入:

    4 10
    w 0.25
    o 0.25
    r 0.25
    d 0.25
    word
    2 10
    a 1.0
    b 0.0
    abc
    2 100
    a 0.312345
    b 0.687655
    abab
    0 0

    输出:

    2.73%
    0.00%
    98.54%

    解题思路:

    对于第一组数据(work)

    很显然算法是:0.25*0.25*0.25*0.25*7*100%=2.73%;

    而对于第三组(abab)就不成立了,为什么呢?

    显然是因为第一组没有重复的字母出现,也就是说,如果你的猴子恰好打下了aba,然后它又不幸地打下了a那么也不算太糟,至少你只需要再打一个bab就可以完成任务了。而对于第一只猴子就没有那么幸运了,如果它打下了wor又不幸地打下了r,那么它必须再打下work才能完成任务。

    也就是说,即使你打下了错误的字母,你也有可能创造了一个前缀。

    所以说我们只需要求出一个错误的字符创造出的前缀是谁,就可以更新这个前缀出现的概率了。

    那么考虑用dp[i][j]表示在猴子打下第i个字母时字符串完成到j的匹配的概率。

    而这个由错误创造的前缀是谁,这是不是KMP。

    然而这和普通的KMP不一样,或者我学了假的KMP,这次kmp的next数组存的是这个模式串第i位的值对应存在的前缀的位置,也就是说,这次是成功指针,而非失配指针。

    dp方程就出来了:dp[这一次敲击][最长匹配的新字符最长前缀]=∑dp[上一次敲击][最长匹配](枚举新字符是谁,再进行前缀操作)

     1 #include<cstdio>
     2 #include<cstring>
     3 #include<algorithm>
     4 using namespace std;
     5 int n,m;
     6 int l;
     7 bool JDr_is_Handsome=true;
     8 char cmd[10];
     9 char a[1001];
    10 int op[1001];
    11 int nxt[1001];
    12 double p[1001];
    13 double dp[1001][30];
    14 int main()
    15 {
    16     while(JDr_is_Handsome)
    17     {
    18         scanf("%d%d",&n,&m);
    19         if(!n&&!m)
    20             return 0;
    21         memset(dp,0,sizeof(dp));
    22         memset(p,0,sizeof(p));
    23         for(int i=1;i<=n;i++)
    24         {
    25             scanf("%s",cmd+1);
    26             op[i]=cmd[1];
    27             scanf("%lf",&p[i]);
    28         }
    29         scanf("%s",a+1);
    30         l=strlen(a+1);
    31         nxt[1]=0;
    32         for(int i=2,j=0;i<=l;)
    33         {
    34             while(j&&a[j+1]!=a[i])j=nxt[j];
    35             if(a[j+1]==a[i])j++;
    36             nxt[i]=j;
    37             i++;
    38         }
    39         dp[0][0]=1.00;
    40         for(int i=1;i<=m;i++)
    41         {
    42             for(int j=0;j<l;j++)
    43             {
    44                 for(int k=1;k<=n;k++)
    45                 {
    46                     int pos=j;
    47                     while(pos&&a[pos+1]!=op[k])
    48                         pos=nxt[pos];
    49                     if(a[pos+1]==op[k])pos++;
    50                     dp[i][pos]+=dp[i-1][j]*p[k];
    51                 }
    52             }
    53         }
    54         double ans=0;
    55         for(int i=l;i<=m;i++)
    56             ans+=dp[i][l];
    57         printf("%.2lf%%
    ",ans*100.00);
    58     }
    59     return 0;
    60 }
  • 相关阅读:
    程序员修炼之道:从小工到专家有感2
    3月13日
    第一次结对作业(2)
    3月12日
    3月11日
    第一次结对作业
    3月10日
    11月6日
    10月28日
    10月7日
  • 原文地址:https://www.cnblogs.com/blog-Dr-J/p/9484738.html
Copyright © 2011-2022 走看看