背景
Bob最近迷上了一个博彩游戏……
描述
这个游戏的规则是这样的:
每花一块钱可以得到一个随机数R,花上N块钱就可以得到一个随机序列;
有M个序列,如果某个序列是产生的随机序列的子串,那么就中奖了,否则不中。
Bob会告诉你这M个序列,和身上有的钱的总数N,当然还有R的范围。
请你告诉Bob中奖的概率有多少?
每花一块钱可以得到一个随机数R,花上N块钱就可以得到一个随机序列;
有M个序列,如果某个序列是产生的随机序列的子串,那么就中奖了,否则不中。
Bob会告诉你这M个序列,和身上有的钱的总数N,当然还有R的范围。
请你告诉Bob中奖的概率有多少?
输入格式
第一行三个用空格隔开的数N、M和R的范围R。
其中1<=R<=9,0<N<=60,0<M<=20000。
下面M行每行一个字符串(长度小于等于20),字符串的每一位范围在1-r之间
保证必要运算都在64位整型范围内。
其中1<=R<=9,0<N<=60,0<M<=20000。
下面M行每行一个字符串(长度小于等于20),字符串的每一位范围在1-r之间
保证必要运算都在64位整型范围内。
输出格式
一行一个实数,表示中奖的概率(保留小数点后5位小数)。
测试样例1
输入
5 1 3
1
输出
0.86831
备注
数据分布:
第1个点~第10个点,每个点5分;
第11个点~第15个点,每个点10分。
对于样例的解释:
随机序列一共有3^5=243个,其中包含"1"的个数为211个,则概率为211/243=0.86831Bob HAN
第1个点~第10个点,每个点5分;
第11个点~第15个点,每个点10分。
对于样例的解释:
随机序列一共有3^5=243个,其中包含"1"的个数为211个,则概率为211/243=0.86831Bob HAN
/* AC自动机+DP f[i][j]表示将字符串扩展到i,在自动机上匹配到j的概率。 因为合法概率较难统计,所以最后答案为 1-不合法的概率。 要滚动一下数组。 */ #include<cstdio> #include<iostream> #include<cstring> #include<queue> #define N 400010 #define eps 0.000000001 using namespace std; int a[N][10],danger[N],point[N],n,m,r,size=1; double f[2][N];char s[N]; queue<int> q; void insert(){ int len=strlen(s),now=1; for(int i=0;i<len;i++){ int t=s[i]-'0'; if(!a[now][t]) a[now][t]=++size; now=a[now][t]; } danger[now]=1; } void build(){ q.push(1);point[1]=0; while(!q.empty()){ int now=q.front();q.pop(); for(int i=1;i<=r;i++){ if(!a[now][i]){ a[now][i]=a[point[now]][i]; continue; } int k=point[now]; while(!a[k][i]) k=point[k]; point[a[now][i]]=a[k][i]; danger[a[now][i]]|=danger[point[a[now][i]]]; q.push(a[now][i]); } } } void dp(){ f[0][1]=1; for(int i=1;i<=n;i++){ for(int j=1;j<=size;j++){ if(danger[j]||f[i+1&1][j]<eps) continue; for(int t=1;t<=r;t++){ int k=j; while(!a[k][t]) k=point[k]; f[i&1][a[k][t]]+=f[i+1&1][j]/(double)r; } } for(int j=1;j<=size;j++) f[i+1&1][j]=0; } } int main(){ scanf("%d%d%d",&n,&m,&r); for(int i=1;i<=r;i++) a[0][i]=1; for(int i=1;i<=m;i++){ scanf("%s",s); insert(); } build();dp(); double ans=0; for(int i=1;i<=size;i++) if(!danger[i]) ans+=f[n&1][i]; printf("%.5f",1.0-ans); return 0; }