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%%\n",ans*100.00);
58     }
59     return 0;
60 }

 

posted @ 2018-08-15 23:44  Unstoppable728  阅读(708)  评论(0编辑  收藏  举报