P2167 [SDOI2009]Bill的挑战
题意:
有n个长度一样的字符串,字符串的每一位是?或者确定的字母,,求与这 N 个串中的刚好 K 个串匹配的字符串 T 的个数
1<=N<=15,1<=|S|<=50
题解:
很明显状态dp
我们可以先预处理一个match[1…len][a…z]:表示第i位取ch的状态,比如match[1]['a]=10101就表示第一个,第三个,第五个字符串的第一位可以取’a‘。预处理出match
f[i][j]表示第i位的匹配上了j这个状态的方案数
转移方程:
j=1010(二进制)就表示:选了第2行和第四行的字符串的状态
选在第i+1行时,要与第i行的状态取&
int now= (match[i][ch - 'a'] & j);
f[i + 1][now]= (f[i + 1][now] + f[i][j]) % mod;
最后枚举答案时,因为题目有说时选取k行,所以枚举所有状态,只有状态中带有k个1的是我们要的状态
代码:
// Problem: P2167 [SDOI2009]Bill的挑战
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P2167
// Memory Limit: 125 MB
// Time Limit: 1000 ms
// Data:2021-08-11 21:21:14
// By Jozky#include <bits/stdc++.h>
#include <unordered_map>
#define debug(a, b) printf("%s = %d\n", a, b);
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> PII;
clock_t startTime, endTime;
//Fe~Jozky
const ll INF_ll= 1e18;
const int INF_int= 0x3f3f3f3f;
template <typename T> inline void read(T& x)
{T f= 1;x= 0;char ch= getchar();while (0 == isdigit(ch)) {if (ch == '-')f= -1;ch= getchar();}while (0 != isdigit(ch))x= (x << 1) + (x << 3) + ch - '0', ch= getchar();x*= f;
}
template <typename T> inline void write(T x)
{if (x < 0) {x= ~(x - 1);putchar('-');}if (x > 9)write(x / 10);putchar(x % 10 + '0');
}
void rd_test()
{
#ifdef ONLINE_JUDGE
#elsestartTime= clock();freopen("in.txt", "r", stdin);
#endif
}
void Time_test()
{
#ifdef ONLINE_JUDGE
#elseendTime= clock();printf("\nRun Time:%lfs\n", (double)(endTime - startTime) / CLOCKS_PER_SEC);
#endif
}
const int maxn= 100;
char s[maxn][maxn];
const int mod= 1e6 + 3;
int f[60][1 << 15];
int match[60][60];
int main()
{//rd_test();int t;read(t);while (t--) {memset(f, 0, sizeof(f));memset(match, 0, sizeof match);int tot= 0;int n, k;cin >> n >> k;for (int i= 1; i <= n; i++)scanf("%s", s[i]);int len= strlen(s[1]);for (int i= 0; i < len; i++) { //对于每一位for (char ch= 'a'; ch <= 'z'; ch++) {for (int k= 1; k <= n; k++) { //对于每个字符串//第k个字符串的第i个位置if (s[k][i] == '?' || s[k][i] == ch) {match[i][ch - 'a']|= (1 << (k - 1));//第i位取ch的状态}}}}int cnt= (1 << n) - 1;f[0][cnt]= 1; //初始状态for (int i= 0; i < len; i++) {for (int j= 0; j <= cnt; j++) {if (f[i][j]) { //如果f第i个位置的j状态有方案for (char ch= 'a'; ch <= 'z'; ch++) {//得到新状态int now= (match[i][ch - 'a'] & j);f[i + 1][now]= (f[i + 1][now] + f[i][j]) % mod;}}}}for (int i= 0; i <= cnt; i++) { //枚举取字符串的所有状态int ans= 0;for (int j= 1; j <= cnt; j<<=1)ans+= (bool)(i&j);if (ans == k) //如果取了k个字符串(i中有k个1)tot= (tot + f[len][i]) % mod;}printf("%d\n", tot);}//Time_test();
}