HDU 3689

额,今天下午又被血虐,看了好几天的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);
        }
    }
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值