QOJ3801:DNA匹配

题意

给定mm个长度为nn的字符串,记ss,且si,j[A,C,G,T,?]s_{i,j} \in [A,C,G,T,?]

随机构建一个仅含[A,C,G,T][A,C,G,T]的长度为nn的字符串tt

tt能和其中任意一个sis_i匹配的概率

ttsis_i匹配定义为存在一种将sis_i?转换的方式使得si=ts_i=t

1n1001 \leq n \leq 100 , 1m401 \leq m \leq 40

概率允许5%5\%的误差,注意P=0P=0不合法

Solution

因为分母过于强大(41004^{100}),正经的概率计算大概率会出问题。

注意到概率误差非常宽松,我们可以乱搞抽样调查

一个tt的贡献可以视为对第一个与tt匹配的sis_i贡献+1+1

所以对于每一个sis_i,构造若干个和sis_i匹配的tt,对sis_i造成贡献仅当ttsis_i匹配但不和s1...i1s_{1...i-1}匹配

设样本空间大小为kk,随机构造能和sis_i匹配的概率为p1p_1,造成贡献的样本数量为ff,则总贡献为

p1×fk\frac{p_1 \times f}{k}

code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
#include<bits/stdc++.h>
using namespace std;
const int mxn=110;
typedef long double ldb;
void cal_t(string a);
bool check(string a,string b);
int n,m;
string s[mxn];
string ver="ACGT";
string t;
ldb val[mxn];
ldb ans;
int main()
{
srand(time(NULL));
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++) cin>>s[i];
for(int i=1;i<=m;i++)
{
ldb u=0,v=1;
bool flag=0;
for(int j=0;j<n;j++) if(s[i][j]!='?') v/=4.0;
for(int p=1;p<=6123;p++)
{
cal_t(s[i]);
flag=0;
for(int k=1;k<i;k++)
{
if(check(s[k],t))
{
flag=1;
break;
}
}
if(!flag) u+=1.0;
}
ans=ans+(u*v/6123.0);
}
printf("%.80Lf\n",ans);
}

bool check(string a,string b)
{
for(int i=0;i<n;i++)
{
if(a[i]=='?') continue;
if(a[i]!=b[i]) return false;
}
return true;
}

void cal_t(string a)
{
t="";
for(int i=0;i<n;i++)
{
char in;
if(a[i]=='?') in=char(ver[(rand()%4)]);
else in=char(a[i]);
t+=in;
}
}
Author: cjh-hhz
Link: https://cjh-hhz.github.io/e3de22399502.html/
Copyright Notice: All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.