题目链接:
https://www.luogu.com.cn/problem/P2167
分析:这道题用的状压dp思路确实不好想,也有人用容斥原理(数学的方法)做出来的不过我是没看懂,太弱了,dp的思路就是我们从第一位开始枚举每一个字母,然后把不匹配的字符串从字符串集状态中删除,枚举完最后一位为止最后得到答案,复杂度50×
2
15
2^{15}
215×50×26,算了一下大约
2
29
2^{29}
229左右,很卡时间,设dp[i][s]表示枚举到第i为合法的字符集状态s有多少种
AC代码:
#include<bits/stdc++.h>
#define RG register
using namespace std;
typedef long long ll;
const int mod = 1000003;
char s[20][50];
ll dp[20][1<<17+1];
int n,k;
/*二进制op有多少个1*/
int get_num(int op)
{
int ans = 0;
while(op)
{
ans++;
op&=(op-1);
}
return ans;
}
void solve()
{
memset(dp,0,sizeof(dp));
cin>>n>>k;
dp[0][(1<<n)-1] = 1;
for(int i=1;i<=n;i++)
{
scanf("%s",s[i]);
}
int len = strlen(s[1]);
/*从第一位开始枚举*/
for(int i=0;i<len;i++)
{
/*枚举字符串集状态*/
for(int op = (1<<n)-1;op>=0;op--)
{
if(dp[i][op])
{
if(get_num(op)<k) continue;
/*每一位的枚举a~z字母*/
for(char str = 'a';str<='z';str++)
{
int tp = op;
/*把不合法的字符串从字符串集状态里面去掉*/
for(int j=1;j<=n;j++)
{
if(s[j][i]!='?'&&s[j][i]!=str) if(tp&(1<<(j-1))) tp-=(1<<(j-1));
}
dp[i+1][tp]+=dp[i][op];
dp[i+1][tp]%=mod;
}
}
}
}
ll ans = 0;
for(int i=1;i<=(1<<n)-1;i++)
{
if(get_num(i)==k)
{
ans+=dp[len][i];
ans%=mod;
}
}
cout<<ans<<'\n';
}
int main()
{
int t;
cin>>t;
while(t--)
solve();
}