额,今天下午又被血虐,看了好几天的kmp总算是看懂了,以前看的不够用心,认真看了后发现还是挺简单的。
这道题挺水的吧,以前做错过的那道CF死活做不出来了,先把这一道水的贴上来。
首先说说这个题意,给你将n个字母打出来的概率,让你求打出一个字符串长度为m的串,其中串s为它的子串的概率。
先特判不可能组成的,再就是dp[i][j],第一维i代表当前达到了第i个字符,第二维代表当前到达了s串的第j个字符。
当打出下一个字符为s[j + 1],是状态转移为dp[i + 1][j + 1] = dp[i][j] * p;当打出下一个字符不是j + 1时,找到它的next数组中的值,即kmp算法,如果找到一个当前串的最大的相等的前后缀,状态转移为dp[i + 1][id + 1] = dp[i][j] * p;如果get_next的返回值为0,需要判断当前字符是否与s串的第一个相等如果相等,状态转移为dp[i + 1][1] = dp[i][j] * p;否则,状态转移为dp[i + 1][0] = dp[i][j] * p。
下面附代码:
#include <cstdio>
#include <iostream>
#include <map>
#include <cstring>
using namespace std;
struct Words
{
char c;
double p;
}w[500];
map<char, double> ma;
char ss[105];
int f[105];
double dp[1005][50];
void make_next()
{
f[0] = 0;
int len = strlen(ss);
for(int i = 1, k = 0; i < len; ++i)
{
while(k > 0 && ss[k] != ss[i])
k = f[k - 1];
if(ss[k] == ss[i])
{
++k;
}
f[i] = k;
}
}
int get_next(int k, char c)
{
while(k > 0 && ss[k] != c)
k = f[k - 1];
return k;
}
int main()
{
int n, m;
while(~scanf("%d%d", &n, &m) && (n || m))
{
memset(f, 0, sizeof(f));
for(int i = 1; i <= 1002; ++i)
for(int j = 0; j <= 26; ++j)
dp[i][j] = 0;
ma.clear();
char s[5];
for(int i = 0; i < n; ++i)
{
double x;
scanf("%s%lf", s, &x);
ma[s[0]] = x;
w[i].c = s[0];
w[i].p = x;
}
scanf("%s", ss);
int len = strlen(ss);
if(len > m)
{
printf("0.00%%\n");
}
else
{
bool error = false;
for(int i = 0; i < len; ++i)
if(!ma.count(ss[i]))
{
error = true;
break;
}
if(error)
{
printf("0.00%%\n");
continue;
}
make_next();
dp[0][0] = 1.0;
double ans = 0;
for(int i = 0; i < m; ++i)
{
for(int j = 0; j < len; ++j)
{
for(int k = 0; k < n; ++k)
if(w[k].c == ss[j])
{
dp[i + 1][j + 1] += dp[i][j] * w[k].p;
}
else
{
int id = get_next(j, w[k].c);
if(id != 0)
dp[i + 1][id + 1] += dp[i][j] * w[k].p;
else if(w[k].c == ss[0])
dp[i + 1][1] += dp[i][j] * w[k].p;
else
dp[i + 1][0] += dp[i][j] * w[k].p;
}
}
}
for(int i = 1; i <= m; ++i)
ans += dp[i][len];
printf("%.2f%%\n", ans * 100);
}
}
}