字典树神仙题目。

由于字符串可以打乱,不好把控当前打印的字符串是哪一个。因此考虑一位一位打印。

比如说我们先考虑打印第一位。此时我们只需要打出来0的个数和第一位所有0的个数一致即可,不需要顺序要求。

但是当打印第二位的时候,我们需要归类第一位是0的字符串和第一位是1的字符串,分别保证匹配一致。

继续递归,我们发现要匹配某前缀对应的所有字符串(无序)。

遍历前缀,字典树。

对于字典树上每一位,计算在所有拥有该前缀的字符串中下一位正确打印的概率。容易发现计算之间互不干扰,答案就是这些概率的成绩。

可以处理字典树每一位被多少字符串覆盖。每一个节点正确打印的概率,就是打印0子树大小个0和1子树大小个1的概率。

组合问题,而且n,m<=1000,想到暴力预处理。dp[i][j]为无序打出i个0和j个1的概率。

转移时,枚举最后一次要打印0还是1就行。

dp[i][j]=max(p*dp[i-1][j]+(1-p)dp[i][j-1],pdp[i][j-1]+(1-p)*dp[i-1][j])

#include<cstdio>
#include<iostream>
using namespace std;
typedef long long ll;
const ll N=1e3+10,M=N*N;
ll a[M][2],c=1,s[M],n,m;//N
double dp[N][N],p,as=1;
char st[N];
int main(){
	scanf("%lld%lld%lf",&n,&m,&p);
	dp[0][0]=1,s[1]=n;
	for(ll i=0;i<=n;i++){
		for(ll j=0;j<=n;j++){
			if(i+j==0) continue;//no
			double t1=0,t2=0;
			if(i) t1+=p*dp[i-1][j],t2+=(1-p)*dp[i-1][j];
			if(j) t1+=(1-p)*dp[i][j-1],t2+=p*dp[i][j-1];
			dp[i][j]=max(t1,t2);
		}
	}
	for(ll i=1;i<=n;i++){
		scanf("%s",&st);
		ll k=1;
		for(ll j=0;j<m;j++){
			if(!a[k][st[j]-'0']) a[k][st[j]-'0']=++c;//no ++c
			k=a[k][st[j]-'0'],s[k]++;
		}
	}
	for(ll i=1;i<=c;i++){
	as*=dp[s[a[i][0]]][s[a[i][1]]];}//+
	printf("%.12lf",as);
	return 0;
}