#include<bits/stdc++.h>
using namespace std;
#define int long long
const int mod = 998244353, maxn = 1e6 + 60000;
string s[405];
string t[405];
int m, siz[405];//siz[i]表示长度大于20的第i个字符串的长度
int res, a[405];//a表示当前选中的字符串的交集
bool bt[25][maxn];//bt[i][j]表示长度为i的 j代表的字符串是否可以匹配
bool check(int len, int x, string s){
for(int j = 0; j < len; j++){
if(s[len - 1 - j] != '?' && (x >> j & 1) != s[len - 1 - j] - '0') return 0;
}
return 1;
}
void dfs(int u, int len, int mark){//当前在第u个字符串,前面选中的字符串长度都是len(都是len才可能有交集),
//mark表示是加还是减(选中奇数个字符串就加,偶数个就减)
if(u == m + 1){
if(len == -1) return;
int p = 1;
for(int i = 0; i < len; i++){
if(a[i] == 2) p = (p * 2) % mod;//找有多少个问号
}
res = (res + mark * p + mod) % mod;//括号内再加个mod,不然结果可能是负数
return;
}
dfs(u + 1, len, mark);//不选当前字符串
if(len == -1){//选当前字符串且该字符串是第一个选中的
len = siz[u];
for(int i = 0; i < siz[u]; i++){
if(t[u][i] == '?') a[i] = 2;
else a[i] = t[u][i] - '0';
}
}
else{
if(siz[u] != len) return;//当前字符串跟之前选的长度不同,不可能有交集,直接return
for(int i = 0; i < len; i++){
if(a[i] == 1 && t[u][i] == '0' || a[i] == 0 && t[u][i] == '1') return;
if(a[i] == 2){
if(t[u][i] == '0') a[i] = 0;
else if(t[u][i] == '1') a[i] = 1;
}
}
}
dfs(u + 1, len, -mark);//选当前字符串,mark取相反数
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
int n, i, j;
cin >> n;
for(i = 1; i <= n; i++){
cin >> s[i];
int len = s[i].size();
if(len <= 20){//长度短的直接暴力枚举所有的字符串,能匹配就标记为1
for(int x = 0; x < (1 << len); x++){
if(check(len, x, s[i])) bt[len][x] = 1;
}
}
else{
t[++m] = s[i];
siz[m] = s[i].size();
}
}
dfs(1, -1, -1);//容斥原理
for(i = 1; i <= 20; i++){
for(j = 0; j < (1 << 20); j++){
res = (res + bt[i][j]) % mod;//统计
}
}
cout << res;
return 0;
}
容斥原理计算匹配的字符串个数
最新推荐文章于 2024-10-02 23:40:03 发布