字典树实际上就是把前缀给缩起来
如果只有两个串,那么答案就是两个串的长度和减去LCP
这就是个容斥的形式,答案就是 2?的数量+∑S∈U(−1)|S|+1LCP(S)
前面的 2?的数量 是根节点
LCP的话,枚举一下每一位,判断一下就可以了
hihocoder跑得好快啊,竟然没被卡常
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <vector>
#include <ctime>
#define pb push_back
using namespace std;
const int N=25,P=998244353;
int n,len[N],pw[25*50],pre[N][55],ipre[55];
char a[N][N<<2];
char *c[N];
int t;
int main(){
freopen("1.in","r",stdin);
freopen("1.out","w",stdout);
pw[0]=1;
for(int i=1;i<=20*50;i++) pw[i]=2*pw[i-1]%P;
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%s",a[i]+1),len[i]=strlen(a[i]+1);
for(int j=len[i];j;j--)
pre[i][j]=pre[i][j+1]+(a[i][j]=='?');
}
int ans=1;
for(int i=1;i<=n;i++) ans=1LL*ans*pw[pre[i][1]]%P;
for(int S=1;S<(1<<n);S++){
int size=0; int mlen=1<<30;
for(int i=1;i<=n;i++)
if((S>>(i-1))&1) c[size++]=a[i],mlen=min(mlen,len[i]);
int cur=0,p=0,prod=1;
ipre[51]=0;
for(int i=50;i;i--){
ipre[i]=ipre[i+1];
for(int j=0;j<size;j++)
ipre[i]+=(c[j][i]=='?');
}
for(int i=1;i<=mlen;i++,p++){
int cnt0=0,cnt1=0;
for(int j=0;j<size;j++){
if(c[j][i]=='0') cnt0++;
if(c[j][i]=='1') cnt1++;
}
if(cnt0 && cnt1) break;
if(!cnt0 && !cnt1){
int pd=1LL*(pw[size]-2)*(i-1)%P;
pd=1LL*pd*pw[ipre[i+1]]%P;
cur=(cur+1LL*prod*pd)%P;
prod=2*prod%P;
continue;
}
int r=size-cnt0-cnt1,pd=1LL*(pw[r]-1)*(i-1)%P;
//for(int j=0;j<size;j++) pd=1LL*pd*pw[pre[c[j]][i+1]]%P;
pd=1LL*pd*pw[ipre[i+1]]%P;
cur=(cur+1LL*prod*pd)%P;
}
int pd=pw[ipre[p+1]];
//for(int j=0;j<size;j++) pd=1LL*pd*pw[pre[c[j]][p+1]]%P;
cur=(cur+1LL*prod*p%P*pd)%P;
for(int i=1;i<=n;i++)
if(!((S>>(i-1))&1)) cur=1LL*cur*pw[pre[i][1]]%P;
if(size&1) ans=(ans+cur)%P;
else ans=(ans-cur)%P;
}
printf("%d\n",(ans+P)%P);
//printf("%d\n",clock());
return 0;
}