题目大意:给你 k 个模板串,然后给你一些字符的出现概率,然后给你一个长度 l ,问你这些字符组成的长度为 l 的字符串不包含任何一个模板串的概率。
思路:AC自动机好题啊!AC自动机建好后,考虑字符一个一个加进去,如果某个节点是单词的节点,那么这个点就是不可走的,设d[ u ][ l ] 表示当前在 u 节点,还剩下 l 步要走的概率,那么很显然 d[ u ][ l ] = SIGMA(d[ v ][ l - 1 ]),v 为 u 的子节点,为了方便,AC自动机建改造的那种。这里还有一个地方需要注意,如何判断一个节点是不是单词节点,用val[ u ] 表示,val == 1 表示是,那么每个 insert 插进去的单词末尾肯定是,还有就是在 get_fail 时,如果 f[ u ] 的 val 为 1,那么 u 的 val 也为1。
唉,看了算法后又坑了好久,WA了半天,最后发现原来是 get_fail 那里广搜时 v 没有 push进去。。。 T^T
代码如下:
#include<cstdio>
#include<cstring>
#include<queue>
#include<algorithm>
using namespace std;
const int SIGMA_SIZE = 256;
const int MAX_NODE = 22*256;
int vis[MAX_NODE][111];
double d[MAX_NODE][111];
int chara[111];
double prob[111];
int n;
struct Ac
{
int ch[MAX_NODE][SIGMA_SIZE];
int fail[MAX_NODE];
int val[MAX_NODE];
int tot;
void init()
{
tot = 1;
val[0] = 0;
memset(ch[0],0,sizeof(ch[0]));
}
void insert(char *s)
{
int len = strlen(s);
int u = 0;
for(int i = 0;i<len;i++)
{
int c = s[i];
if(!ch[u][c])
{
val[tot] = 0;
memset(ch[tot],0,sizeof(ch[tot]));
ch[u][c] = tot++;
}
u = ch[u][c];
}
val[u] = 1;
}
void get_fail()
{
queue <int> q;
fail[0] = 0;
for(int i = 0;i<SIGMA_SIZE;i++)
{
int u = ch[0][i];
if(u)
{
q.push(u);
fail[u] = 0;
}
}
while(!q.empty())
{
int u = q.front();
q.pop();
for(int i = 0;i<SIGMA_SIZE;i++)
{
int v = ch[u][i];
if(!v)
{
ch[u][i] = ch[fail[u]][i];
continue;
}
q.push(v);
int j = fail[u];
while(j && !ch[j][i]) j = fail[j];
j = ch[j][i];
fail[v] = j;
val[v] |= val[fail[v]];
}
}
}
double get_prob(int u,int l)
{
if(!l) return 1;
if(vis[u][l]) return d[u][l];
vis[u][l] = 1;
double &ans = d[u][l];
ans = 0;
for(int i = 0;i<n;i++)
if(!val[ch[u][chara[i]]]) ans += prob[i] * get_prob(ch[u][chara[i]],l-1);
//printf("u = %d,l = %d,ans = %f\n",u,l,ans);
return ans;
}
}ac;
char str[22][22];
int main()
{
int _;
scanf("%d",&_);
for(int cas = 1;cas <= _ ;cas ++)
{
ac.init();
int k;
scanf("%d",&k);
for(int i = 0;i<k;i++)
{
scanf("%s",str[i]);
ac.insert(str[i]);
}
scanf("%d",&n);
char tt[5];
for(int i = 0;i<n;i++)
{
scanf("%s%lf",tt,&prob[i]);
chara[i] = tt[0];
}
int l;
scanf("%d",&l);
ac.get_fail();
memset(vis,0,sizeof(vis));
printf("Case #%d: %.6f\n",cas,ac.get_prob(0,l));
}
return 0;
}
/*
11
2
ab
ab
2
c 0.5
d 0.5
2
*/