约定: N ≤ 15 , ∣ S i ∣ ≤ 50 N \leq 15, |S_i| \leq 50 N≤15,∣Si∣≤50
我们不能直接枚举状态 S ∈ [ 0 , 2 15 − 1 ] S \in [0,2^{15} - 1] S∈[0,215−1] 来表示与这些字符串匹配的有多少个 T T T,因为可能出了状态里面选中的字符串 S S S,那些没被选中的也被匹配了。例如: S 1 = a ? , S 2 = ? a , T = a a S_1 = a?,S_2 = ?a,T = aa S1=a?,S2=?a,T=aa,如果我们只要和 S 1 S_1 S1 匹配, T T T 是符合的,但是同时 T T T 也和 S 2 S_2 S2 匹配了。
我们考虑枚举每一位选择的字母 ′ a ′ → ′ z ′ 'a' \rightarrow 'z' ′a′→′z′,如果这样选,匹配状态会怎样更新。那么我们就需要之前的匹配状态,定义: d p [ i ] [ s ] dp[i][s] dp[i][s] 为处理到第 i i i 个位置,且匹配状态为 s s s 时的方案数。那么最终答案就是: ∑ d p [ l e n ] [ S ] \sum dp[len][S] ∑dp[len][S],其中 S S S 的二进制 1 1 1 的个数恰好为 k k k
初始化 d p [ 0 ] [ 2 n − 1 ] = 1 dp[0][2^{n} -1] = 1 dp[0][2n−1]=1,还没开始匹配时所有的字符串都匹配。
转移:对于当前选择的字符 c c c,设它在位置 i i i 的匹配状态为: m a t c h [ i ] [ c ] match[i][c] match[i][c] (用二进制来看),这个信息我们预处理出来。
那么转移就是: d p [ i ] [ S ] → d p [ i + 1 ] [ S dp[i][S] \rightarrow dp[i + 1][S dp[i][S]→dp[i+1][S & m a t c h [ i ] [ c ] ] match[i][c]] match[i][c]]
时间复杂度: O ( l e n × 2 N × 26 ) O(len \times 2^{N} \times 26) O(len×2N×26)
#include
#define fore(i,l,r) for(int i=(int)(l);i<(int)(r);++i)
#define fi first
#define se second
#define endl '\n'
#define ull unsigned long long
#define ALL(v) v.begin(), v.end()
#define Debug(x, ed) std::cerr << #x << " = " << x << ed;
const int INF=0x3f3f3f3f;
const long long INFLL=1e18;
typedef long long ll;
template<class T>
constexpr T power(T a, ll b){
T res = 1;
while(b){
if(b&1) res = res * a;
a = a * a;
b >>= 1;
}
return res;
}
constexpr ll mul(ll a,ll b,ll mod){ //快速乘,避免两个long long相乘取模溢出
ll res = a * b - ll(1.L * a * b / mod) * mod;
res %= mod;
if(res < 0) res += mod; //误差
return res;
}
template<ll P>
struct MLL{
ll x;
constexpr MLL() = default;
constexpr MLL(ll x) : x(norm(x % getMod())) {}
static ll Mod;
constexpr static ll getMod(){
if(P > 0) return P;
return Mod;
}
constexpr static void setMod(int _Mod){
Mod = _Mod;
}
constexpr ll norm(ll x) const{
if(x < 0){
x += getMod();
}
if(x >= getMod()){
x -= getMod();
}
return x;
}
constexpr ll val() const{
return x;
}
explicit constexpr operator ll() const{
return x; //将结构体显示转换为ll类型: ll res = static_cast(OBJ)
}
constexpr MLL operator -() const{ //负号,等价于加上Mod
MLL res;
res.x = norm(getMod() - x);
return res;
}
constexpr MLL inv() const{
assert(x != 0);
return power(*this, getMod() - 2); //用费马小定理求逆
}
constexpr MLL& operator *= (MLL rhs) & { //& 表示“this”指针不能指向一个临时对象或const对象
x = mul(x, rhs.x, getMod()); //该函数只能被一个左值调用
return *this;
}
constexpr MLL& operator += (MLL rhs) & {
x = norm(x + rhs.x);
return *this;
}
constexpr MLL& operator -= (MLL rhs) & {
x = norm(x - rhs.x);
return *this;
}
constexpr MLL& operator /= (MLL rhs) & {
return *this *= rhs.inv();
}
friend constexpr MLL operator * (MLL lhs, MLL rhs){
MLL res = lhs;
res *= rhs;
return res;
}
friend constexpr MLL operator + (MLL lhs, MLL rhs){
MLL res = lhs;
res += rhs;
return res;
}
friend constexpr MLL operator - (MLL lhs, MLL rhs){
MLL res = lhs;
res -= rhs;
return res;
}
friend constexpr MLL operator / (MLL lhs, MLL rhs){
MLL res = lhs;
res /= rhs;
return res;
}
friend constexpr std::istream& operator >> (std::istream& is, MLL& a){
ll v;
is >> v;
a = MLL(v);
return is;
}
friend constexpr std::ostream& operator << (std::ostream& os, MLL& a){
return os << a.val();
}
friend constexpr bool operator == (MLL lhs, MLL rhs){
return lhs.val() == rhs.val();
}
friend constexpr bool operator != (MLL lhs, MLL rhs){
return lhs.val() != rhs.val();
}
};
const ll mod = 1000003;
using Z = MLL<mod>;
std::string s[20];
int main(){
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cout.tie(nullptr);
int t;
std::cin >> t;
while(t--){
int n, k;
std::cin >> n >> k;
fore(i, 0, n) std::cin >> s[i];
int len = s[0].size();
std::vector<std::vector<int>> match(len + 1, std::vector<int>(27, 0));
fore(p, 0, len)
fore(i, 0, n)
for(char c = 'a'; c <= 'z'; ++c)
if(s[i][p] == c || s[i][p] == '?')
match[p][c - 'a'] |= (1 << i);
std::vector<std::vector<Z>> dp(len + 1, std::vector<Z>(1 << n | 1, 0));
dp[0][(1 << n) - 1] = 1;
fore(i, 0, len)
fore(S, 0, 1 << n)
if(dp[i][S].x)
for(char c = 'a'; c <= 'z'; ++c)
dp[i + 1][S & match[i][c - 'a']] += dp[i][S];
Z ans = 0;
fore(S, 0, 1 << n)
if(__builtin_popcount(S) == k) //恰好与k个字符串匹配
ans += dp[len][S];
std::cout << ans << endl;
}
return 0;
}