[SDOI2009] Bill的挑战
题目描述
Sheng_bill 不仅有惊人的心算能力,还可以轻松地完成各种统计。在昨天的比赛中,你凭借优秀的程序与他打成了平局,这导致 Sheng_bill 极度的不满。于是他再次挑战你。这次你可不能输。
这次,比赛规则是这样的:
给出 \(N\) 个长度相同的字符串(由小写英文字母和 ?
组成),\(S_1,S_2,\dots,S_N\),求与这 \(N\) 个串中的刚好 \(K\) 个串匹配的字符串 \(T\) 的个数,答案对 \(1000003\) 取模。
若字符串 \(S_x(1\le x\le N)\) 和 \(T\) 匹配,满足以下条件:
- \(|S_x|=|T|\)。
- 对于任意的 \(1\le i\le|S_x|\),满足 \(S_x[i]= \texttt{?}\) 或者 \(S_x[i]=T[i]\)。
其中 \(T\) 只包含小写英文字母。
输入格式
本题包含多组数据。
第一行一个整数 \(T\),表示数据组数。
对于每组数据,第一行两个整数,\(N\) 和 \(K\)。
接下来 \(N\) 行,每行一个字符串 \(S_i\)。
输出格式
每组数据输出一行一个整数,表示答案。
样例 #1
样例输入 #1
5
3 3
???r???
???????
???????
3 4
???????
?????a?
???????
3 3
???????
?a??j??
????aa?
3 2
a??????
???????
???????
3 2
???????
???a???
????a??
样例输出 #1
914852
0
0
871234
67018
提示
数据规模与约定
- 对于 \(30\%\) 的数据,\(N\le5\),\(|S_i|\le20\);
- 对于 \(70\%\) 的数据,\(N\le13\),\(|S_i|\le30\);
- 对于 \(100\%\) 的数据,\(1\le T\le 5\),\(1\le N \le15\),\(1\le|S_i|\le50\)。
这题,肯定状压DP
但是长度<=50故我们必须换一种思路,状态压缩N个字符串匹配状态
\(我们定义f[i][j]为T串已经匹配了i位,且与n个字符串是否匹配的集合为j\)
然后预处理出T每一位上从[a~z]的情况下与N个字符串的匹配情况
然后动态转移方程为
\(F[i][j\And ac[i][k]]=f[i][j\And ac[i][k]]+f[i-1][j]\)
点击查看代码
#include <bits/stdc++.h>
using namespace std;
const int N =1005,mod=1000003;
void T(int x){bitset<10> a;a=x;cout<<a<<endl;}
int t,n,k,f[55][(1<<15)+5],AC[55][30];
string s[20];
int main()
{
ios_base::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
cin>>t;
while(t--)
{
cin>>n>>k;
for(int i=1;i<=n;i++)
{
s[i].clear();
cin>>s[i];
}
if(k>n)
{
cout<<0<<endl;
continue;
}
int len=s[1].size();
memset(f,0,sizeof(f));
memset(AC,0,sizeof(AC));
for(int i=0;i<len;i++)
{
for(char ch='a';ch<='z';ch++)
{
for(int k=1;k<=n;k++)
{
if(s[k][i]=='?'||s[k][i]==ch)
{
AC[i][ch-'a']|=(1<<(k-1));
}
}
}
}
f[0][(1<<n)-1]=1;
for(int i=0;i<len;i++)
{
for(int j=0;j<(1<<n);j++)
{
if(f[i][j])
for(int k=0;k<26;k++)
{
f[i+1][j&AC[i][k]]=(f[i+1][j&AC[i][k]]+f[i][j])%mod;
}
}
}
int ans=0;
for(int i=0;i<(1<<n);i++)
{
int tot=0;
for(int j=0;j<n;j++)
{
if(i&(1<<j))tot++;
}
if(tot==k)ans=(ans+f[len][i])%mod;
}
cout<<ans<<endl;
}
return 0;
}