- 题意:给你n个长度为l的字符串,
1<=n<=50
,
1<=l<=20
,选出一个字符串,每一次操作你可以知道这个字符串某个位置的字符,问多少次可以操作可以唯一确定这个字符串是哪个。求出对于所有字符串,可以唯一确定一个字符串的操作数的期望。
- 思路:这是一个概率题,概率题最重要的就是不重复不遗漏。因为这里l的长度只有20,所以可以考虑枚举每一次猜的哪个位置。比如对于abc,abd这两个字符串,知道1,2两个位置都是不能知道具体是哪一个的,可以看出在一个字符串不能被确定的时候,前面的操作的顺序是无关的,因此枚举所有的操作的复杂度为
l∗2l
。确定一个特定的操作能确定哪些字符串可以使用状态压缩的方法来解决。具体实现见代码以及注释。
- 代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int MAXN = 50 + 5;
const int MAXM = 20;
LL identity[1<<MAXM];
int n;
char s[MAXN][MAXM + 10];
int main()
{
scanf("%d", &n);
getchar();
for(int i = 0; i < n; i++) {
scanf("%s", s[i]);
}
int len = strlen(s[0]);
for(int i = 0; i < n; i++) {
for(int j = i + 1; j < n; j++) {
int mask = 0;
for(int k = 0; k < len; k++) {
if(s[i][k] == s[j][k]) {
mask |= (1<<k);
}
}
identity[mask] |= (1LL<<i)|(1LL<<j);
}
}
for(int mask = (1<<len) - 1; mask >= 0; mask--) {
for(int i = 0; i < len; i++) {
if((mask>>i)&1) {
identity[mask^(1<<i)] |= identity[mask];
}
}
}
long double ans = 0;
for(int mask = 0; mask < (1<<len); mask++) {
int moves = __builtin_popcount(mask) + 1;
for(int k = 0; k < len; k++) {
if(!((mask>>k)&1)) {
LL dif = identity[mask]^identity[mask^(1<<k)];
if(dif == 0) continue;
int cnt = __builtin_popcountll(dif);
long double curexp = cnt * moves;
for(int i = 0; i < moves - 1; i++) {
curexp *= (long double)(moves - i - 1) / (long double)(len - i);
}
curexp /= (long double)(len - moves + 1);
ans += curexp;
}
}
}
ans /= (long double)n;
cout << fixed << setprecision(15) << ans << endl;
}