题目描述 Description
Sheng bill不仅有惊人的心算能力,还可以轻松地完成各种统计。在昨天的比赛中,你凭借优秀的程序与他打成了平局,这导致Sheng bill极度的不满。于是他再次挑战你。这次你可不能输!
这次,比赛规则是这样的:
给N个长度相同的字符串(由小写英文字母和′?′组成),S1,S2,...,SN,求与这N个串中的刚好K个串匹配的字符串T的个数(答案模1000003)。
若字符串Sx(1 ≤ x ≤ N)和T匹配,满足以下条件:
1. Sx.length = T.length。
2. 对于任意的1 ≤ i ≤ Sx.length,满足Sx[i]=′?′或者Sx[i]= T[i]。
其中T只包含小写英文字母。
输入描述 Input Description
本题包含多组数据。
第一行:一个整数T,表示数据的个数。
对于每组数据:
第一行:两个整数,N和K(含义如题目表述)。
接下来N行:每行一个字符串。
输出描述 Output Description
对于每组数据,输出方案数目(共T行)。
样例输入 Sample Input
1
2 1
a?
?b
样例输出 Sample Output
50
数据范围及提示 Data Size & Hint
对于30%的数据,T ≤ 5,N ≤ 5,字符串长度≤ 20;
对于70%的数据,T ≤ 5,N ≤ 13,字符串长度≤ 30;
对于100%的数据,T ≤ 5,N ≤ 15,字符串长度≤ 50。
写代码用一个小时,dp转移写错,调了一个多小时,把lsany写成any,半小时改过来一个,半小时后发现还有一个没改过来。别人写的程序比我短一半,结果后几个点效率还比我好(前几个比我差)。存天理,灭蒟蒻啊。
好了,我们来看一下这一个题目。我们可以一个一个字母的枚举,在枚举的过程中,我们不妨进行如下的动态规划。f[i][j]表示,当前已经处理了i位,匹配状态为j。j是我们用状态压缩得到的一个数字,就是用n个二进制位,分别表示一个字符串是否被匹配。
状态转移的时候,我们把j所表示的已经被匹配的字符串分离出来,然后处理他们的下一位。建一个动态数组,用来储存下一位是X的字符串有哪几个。处理完后,将所有下一位是?的字符串直接判定为被匹配,然后枚举下一个字母,进行动态转移。
输出的时候,枚举第几位被匹配就好。
代码:
1 #include<iostream> 2 #include<cstring> 3 #include<string> 4 #include<vector> 5 using namespace std; 6 7 const int mod = 1000003; 8 9 int f[51][65536]; 10 int t, n, len, k; 11 int ans; 12 vector<int> G[27]; //动态数组 13 char c[16][52]; 14 15 void dp() { 16 memset(f, 0, sizeof(f)); 17 int num = 0; 18 for (int i = 0; i < 27; i++) //预先处理第一位 19 G[i].clear(); 20 for (int i = 1; i <= n; i++) { 21 if (c[i][0] == ‘?‘) 22 G[26].push_back(i); 23 else G[(int)c[i][0] - 97].push_back(i); 24 } 25 int any = 0; 26 for (int i = 0; i < G[26].size(); i++) 27 any = any + (1 << (G[26][i] - 1)); //any表示下一位为?的字符串的匹配状态 28 for (int i = 0; i < 26; i++) { 29 int lsany = any; //lsany表示分别下一位为a - z时的匹配状体 30 for (int j = 0; j < G[i].size(); j++) 31 lsany = lsany + (1 << (G[i][j] - 1)); 32 if (lsany != 0) 33 f[1][lsany] = f[1][lsany] % mod + 1; //动态转移 34 if (lsany > num) num = lsany; 35 } 36 for (int i = 1; i < len; i++) 37 for (int j = 1; j <= num; j++) 38 if (f[i][j] != 0) { 39 for (int k = 0; k < 27; k++) 40 G[k].clear(); 41 for (int k = 0; k < n; k++) 42 if ((1 & j >> k) == 1) 43 if (c[k+1][i] == ‘?‘) 44 G[26].push_back(k+1); 45 else G[(int) c[k+1][i] - 97].push_back(k+1); 46 int any = 0; 47 for (int k = 0; k < G[26].size(); k++) 48 any = any + (1 << (G[26][k] - 1)); 49 for (int k = 0; k < 26; k++) { 50 int lsany = any; 51 for (int w = 0; w < G[k].size(); w++) 52 lsany = lsany + (1 << (G[k][w] - 1)); 53 if (lsany != 0) 54 f[i+1][lsany] =(f[i+1][lsany] + f[i][j]) % mod; 55 } 56 } 57 } 58 59 60 void print(int now, int where, int w){ //输出 61 if (now == k) {ans =(ans + f[len][w]) % mod; return;} 62 if (where > n) return; 63 print(now+1, where+1, w + (1 << (where - 1))); 64 print(now, where+1, w); 65 } 66 67 int main() { 68 ios::sync_with_stdio; 69 cin >> t; 70 for (int o = 0; o < t; o++) { 71 cin >> n >> k; 72 for (int i = 1; i <= n; i++) 73 cin >> c[i]; 74 len = 0; 75 while ((int)c[1][len] > 96 || c[1][len] == ‘?‘) 76 len++; 77 dp(); 78 ans = 0; 79 print(0, 1, 0); 80 cout << ans << "\n"; 81 } 82 return 0; 83 }