这题把AC自动机和dp结合了下,题意就是给你一些模式串,给你选择每个字符的概率,让你选择L个字符,求不出现模式串的概率。
先把模式串都插入AC自动机,用一个match数组记录该节点是否是单词节点,特别注意下这里单词节点可能是从0节点开始,也肯能由后缀构成单词节点,所以得加一句match[i]|=match[f[i]],即失配指针指向的点如果是单词节点也可以构成改点。
这题的dp思想比较简单:
dp[i][j]表示还有i个字母要选,当前在j这个节点。
dp[i][j]=sum{p[c]*dp[i-1][ch[j][c]] } 搜索当前节点的所有不是单词节点的子节点,乘以其概率求和。
代码:
#include<iostream>
#include<cstdio>
#include<vector>
#include<string>
#include<queue>
#include<cmath>
#include<algorithm>
#include<cstring>
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define maxn 10005
#define INF 0xfffffff
#define mem(a,b) memset(a,b,sizeof(a))
#define FOR(i,s,t) for(int i=s;i<=t;i++)
#define ull unsigned long long
#define ll long long
#define M 62
#define N 444
using namespace std;
int getnum(char ch)
{
if('0'<=ch&&ch<='9') return ch-'0';
else if('A'<=ch&&ch<='Z') return ch-'A'+10;
else
return ch-'a'+36;
}
struct AC
{
int f[N],ch[N][M];
bool match[N];
int tot;
void init()
{
tot=0;
f[0]=match[0]=0;
memset(ch[0],0,sizeof(ch[0]));
}
void insert(char *s)
{
int l=strlen(s);
int p=0;
for(int i=0;i<l;i++)
{
int c=getnum(s[i]);
if(!ch[p][c])
{
ch[p][c]=++tot;
f[tot]=0;match[tot]=0;memset(ch[tot],0,sizeof(ch[tot]));
}
p=ch[p][c];
}
match[p]=1;
}
void getfail()
{
queue<int> q;
for(int i=0;i<M;i++)
{
int u=ch[0][i];
if(u)
{
f[u]=0;
q.push(u);
}
}
while(!q.empty())
{
int r=q.front();q.pop();
match[r]|=match[f[r]];//如果失配指针指向的位置是匹配串,那么这里也应该是
for(int i=0;i<M;i++)
{
int u=ch[r][i];
if(!u)
{
ch[r][i]=ch[f[r]][i];continue;
}
q.push(u);
f[u]=ch[f[r]][i];
}
}
}
}ac;
char s[22][22];
double p[100];
double dp[105][N];
bool vis[105][N];
double DP(int i,int j)
{
if(!i) return 1.0;
if(vis[i][j]) return dp[i][j];
vis[i][j]=1;
dp[i][j]=0;
for(int c=0;c<62;c++)
{
if(!ac.match[ac.ch[j][c]]) dp[i][j]+=p[c]*DP(i-1,ac.ch[j][c]);
}
return dp[i][j];
}
void init()
{
memset(p,0,sizeof(p));
memset(vis,0,sizeof(vis));
}
int main()
{
int t,tt=1;
scanf("%d",&t);
while(t--)
{
ac.init();
int k,n;
scanf("%d",&k);
for(int i=1;i<=k;i++)
{
scanf("%s",s[i]);
ac.insert(s[i]);
}
ac.getfail();
init();
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
char ch[5];
double tp;
scanf("%s",ch); scanf("%lf",&tp);
p[getnum(ch[0])]=tp;
}
int l;
scanf("%d",&l);
printf("Case #%d: %.6lf\n",tt++,DP(l,0));
}
return 0;
}