[luogu]P2167 [SDOI2009]Bill的挑战
原题链接:P2167 [SDOI2009]Bill的挑战
题意
有$n(\le 15)$个等长字符串,由小写字母和'?'构成,'?'任意匹配。
求有多少个字符串与刚好$k$个匹配。
分析
做了好久然后发现没有这么难,想复杂了。
原来想法是$f[i][j][S]$表示匹配到前$i$个字母,第$i$个字母为$j$,最大匹配集合为$S$的方案数。
然后发现第二个条件没有用。删掉就行了。
状态转移方程?
对于每个$S$,枚举下一个字母,然后与$S$中的串进行匹配,寻找交集,直接加上去就行了。
代码
1 #include <bits/stdc++.h> 2 #define Mid ((l + r) >> 1) 3 #define lson (rt << 1) 4 #define rson (rt << 1 | 1) 5 using namespace std; 6 const int mod = 1e6 + 3; 7 int read(){ 8 char c; int num, f = 1; 9 while(c = getchar(),!isdigit(c)) if(c == '-') f = -1; num = c - '0'; 10 while(c = getchar(), isdigit(c)) num = num * 10 + c - '0';; 11 return f * num; 12 } 13 int n, m, k, f[59][1 << 15], len[59], maxn; 14 int a[15][59], pcnt[1 << 15]; 15 char c[59]; 16 void up(int &a, int b){ 17 a = (a + b); 18 if(a > mod) a -= mod; 19 } 20 void work(){ 21 memset(f, 0, sizeof(f)); 22 n = read(); k = read(); 23 for(int i = 0; i < n; i++){ 24 scanf("%s", c + 1); 25 m = strlen(c + 1); 26 for(int j = 1; j <= m; j++){ 27 if(c[j] == '?') a[i][j] = 26; 28 else a[i][j] = c[j] - 'a'; 29 } 30 } 31 for(int i = 0; i < 1 << n; i++) 32 pcnt[i] = pcnt[i >> 1] + (i & 1); 33 f[0][(1 << n) - 1] = 1; 34 for(int i = 0; i < m; i++){ 35 for(int s = 0; s < 1 << n; s++)if(f[i][s]){ 36 for(int j = 0; j < 26; j++){ 37 int mask = 0; 38 for(int p = 0; p < n; p++) if(s & 1 << p) 39 if(a[p][i + 1] == 26 || a[p][i + 1] == j) 40 mask |= 1 << p; 41 up(f[i + 1][mask], f[i][s]); 42 } 43 } 44 } 45 int ans = 0; 46 for(int i = 0; i < 1 << n; i++){ 47 if(pcnt[i] == k) 48 up(ans, f[m][i]); 49 50 } 51 printf("%d\n", ans); 52 } 53 main() 54 { 55 int Case = read(); 56 while(Case--) work(); 57 return 0; 58 } 59 /* 60 f[i][s]表示匹配到前i位,匹配的状态为s的方案数 61 62 */