whu1572 Cyy and Fzz[字符串+概率]

1 篇文章 0 订阅
http://acm.whu.edu.cn/land/problem/detail?problem_id=1572


题意:给定n(<=8)个单词和一个长度L(<=14)

问随机写一个L长度的字符串(a..z等概率出现),出现所给单词个数的期望。


两种思路:


------------KMP------------

分别求每一个单词出现的概率相加即可。

求一个单词出现的概率不好求,可以转化为求其不出现的概率

对于某给定单词s,

用dp[i][j][k] 表示长度为i的随机字符串最后一个字符为k,最后k个字符与s[1..k]相等的概率

转移的时候控制k<|s|就可以保证s在随机字符串中不出现。

转移的时候要用到kmp,因为随机字符串加上的字符后可能导致和s[k+1]不匹配

这时候要用kmp来跳到一个合适的匹配位置。

例如,s = "abaa", 

有状态dp[i][3]['a'], 表示长度为i的随机字符串后3个字符为"aba"

现在在后面加一个字符'b', 得到四个字符"abab", 和s匹配的长度变成了2

所以要转移到dp[i+1][2]['b']

#include <cstdio>
#include <cstring>
using namespace std;
const double PP = 1.0/26.0;
const double eps = 1e-8;
int n, L;
int next[20], a[20];
double f[20][30][30];
char s[20];
double solve(char s[]){
    int n = strlen(s+1);
    for (int i=1; i<=n; i++) a[i] = s[i]-'a';
    next[1] = 0;
    for (int i=2; i<=n; i++){
        int j = next[i-1];
        while (j && a[j+1] != a[i]) j = next[j];
        if (a[j+1] == a[i]) j++;
        next[i] = j;
    }
    memset(f, 0, sizeof(f));
    for (int c=0; c<26; c++)
        f[1][c][c==a[1]] = PP;
    for (int i=1; i<L; i++)
    for (int j=0; j<26; j++)
    for (int k=0; k<n&&k<=i; k++) if (f[i][j][k]>eps){
        for (int c=0; c<26; c++){
            int nxtj = k;
            while (nxtj && a[nxtj+1]!=c) nxtj = next[nxtj];
            if (a[nxtj+1] == c) nxtj++;
            f[i+1][c][nxtj] += f[i][j][k]*PP;
        }
    }
    double val = 0;
    for (int j=0; j<26; j++)
        for (int k=0; k<n; k++)
            val += f[L][j][k];
    return 1.0-val;
}
int main(){
    int test;
    scanf("%d", &test);
    for (int T=1; T<=test; T++){
        double ans = 0;
        scanf("%d %d", &n, &L);
        for (int i=1; i<=n; i++){
            scanf("%s", s+1);
            ans += solve(s);
        }
        printf("%.6lf\n", ans+eps);
    }
    return 0;
}



------------AC自动机------------

用n个字符串建一个自动机,单词结尾出的节点存下单词编号。

然后从根节点开始走,

dp[i][j][k]表示走i步, 走到j, 经过了k这些单词, k是二进制数

最后答案等于 sigma(dp[L][j][k] * bit(k)), bit(k)表示k中1的个数。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值