UVA11468 AC自动机+记忆化搜索



题意:给定一些字符及其各自对应的选择概率,随机选择L次后将得到一个长度为L的随机字符串S(每次独立随机)。给出K个模板串,计算S不包含任何一个串的概率(即任意一个模板串都不是S的连续子串)


分析:

构造AC自动机,每次随机生成一个字母,相当于在AC自动机随机走一步。所以有单词标记的结点标记为“禁止”。本题就是求从结点0走L步,不进入任何禁止结点的概率。

假设d(i,j)表示在当前结点i,还需要走j步,不碰到任何禁止结点的概率。求解过程用记忆化搜索即可。


代码如下:

 

#include <cstdio>
#include <cstring>
#include <string>
#include <queue>
#include <map>
using namespace std;

const int sigma_size = 64;
const int maxn = 500; //结点总数
const int maxs = 20+10; //模板个数
const int maxL = 105;
int idx[256];
double pro[sigma_size];
int K,L,n;
int sz; //结点总数
char p[maxs][maxs];
int ch[maxn][sigma_size];
int fail[maxn]; //fail函数
int match[maxn]; //是否包含某个字符串
queue<int>que;
double d[maxn][maxL];
bool vis[maxn][maxL];

void Insert(char *s){
    int u = 0, len = strlen(s);
    int c;
    for (int i=0; i<len; i++){
        c = idx[s[i]];
        if (!ch[u][c]){
            memset(ch[sz],0,sizeof(ch[sz]));
            match[sz] = 0;
            ch[u][c] = sz++;
        }
        u = ch[u][c];
    }
    match[u] = 1;
}

void getFail(){
   while (!que.empty()) que.pop();
   fail[0] = 0;

   int u,r,v;
   for (int c=0; c<sigma_size; c++){
       u = ch[0][c];
       if (u) {fail[u] = 0; que.push(u);}
   }

   while (!que.empty()){
        r = que.front(); que.pop();
        for (int c=0; c<sigma_size; c++){
            u = ch[r][c];
            if (!u) {ch[r][c] = ch[fail[r]][c]; continue;}
            que.push(u);
            v = fail[r];
            while (v && !ch[v][c]) v = fail[v];
            fail[u] = ch[v][c];
            match[u] |= match[fail[u]];
        }
   }
}

void init(){
   char cc[9];
   scanf("%d",&K);
   for (int i=0; i<K; i++) scanf("%s",p[i]);
   scanf("%d",&n);
   for (int i=0; i<n; i++) {
       scanf("%s %lf",cc,&pro[i]);
       idx[cc[0]] = i;
   }

   sz = 1;
   memset(ch[0],0,sizeof(ch[0]));
   for (int i=0; i<K; i++) Insert(p[i]);
   getFail();

   scanf("%d",&L);
   memset(vis,0,sizeof(vis));
}

double getProb(int u, int L){
   if (!L) return 1.0;
   if (vis[u][L]) return d[u][L];
   vis[u][L] = 1;
   double &ans = d[u][L];
   ans = 0.0;
   for (int i=0; i<n; i++) if (!match[ch[u][i]]) ans += pro[i]*getProb(ch[u][i],L-1);
   return ans;
}

int main(){
    int kase = 0;
    int T;
    scanf("%d",&T);
    while (T--){
        init();
        printf("Case #%d: %.6lf\n",++kase,getProb(0,L));
    }
    return 0;
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值