题目链接:点击打开链接
思路:
用d[i][j]表示前i个字符,已经匹配了字母中的j个字符,最终包含这个字母的概率。
每次转移的时候有n个方向, 表示第i个字符选哪个字符, 那么有个问题, 如果我当前选的这个字符失配了, 那么转移之后我还匹配了多少个字符。 这恰恰是KMP能做的。
细节参见代码:
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
#include <string>
#include <vector>
#include <stack>
#include <bitset>
#include <cstdlib>
#include <cmath>
#include <set>
#include <list>
#include <deque>
#include <map>
#include <queue>
#define Max(a,b) ((a)>(b)?(a):(b))
#define Min(a,b) ((a)<(b)?(a):(b))
using namespace std;
typedef long long ll;
typedef long double ld;
const double eps = 1e-6;
const double PI = acos(-1);
const int mod = 1e9 + 7;
const int INF = 0x3f3f3f3f;
const int seed = 131;
const ll INF64 = ll(1e18);
const int maxn = 1000 + 10;
int n, m, vis[maxn][13], kase = 0, len, f[13];
double d[maxn][13];
struct node {
char c; double p;
}a[100];
char p[13];
void getfail() {
f[0] = 0; f[1] = 0;
for(int i = 1; i < len; i++) {
int j = f[i];
while(j && p[i] != p[j]) j = f[j];
f[i+1] = p[i] == p[j] ? j+1 : 0;
}
}
double dp(int i, int j) {
double& ans = d[i][j];
if(i == m) return 0;
if(vis[i][j] == kase) return ans;
vis[i][j] = kase;
ans = 0;
for(int k = 1; k <= n; k++) {
int cur = j;
while(cur && p[cur] != a[k].c) cur = f[cur];
if(p[cur] == a[k].c) {
if(cur >= len-1) ans += a[k].p;
else ans += dp(i+1, cur+1)*a[k].p;
}
else ans += dp(i+1, cur) * a[k].p;
}
return ans;
}
int main() {
while(~scanf("%d%d", &n, &m)) {
if(n == 0 && m == 0) return 0;
for(int i = 1; i <= n; i++) {
scanf("%s%lf", p, &a[i].p);
a[i].c = p[0];
}
scanf("%s", p);
len = strlen(p);
getfail();
++kase;
double ans = dp(0, 0) * 100;
ans *= 100;
double cur = round(ans);
printf("%.2f%%\n", (double)cur/100.0);
}
return 0;
}