想到了字典树求解,但是TLE了,后来分析发现当询问的字符串中"_"的个数一多的话,我这个算法很容易就超时。不过因为我觉得这个算法其实还是可以的,而且以前也有过这种情况,所以就分类讨论,当询问的字符串中的“1”或“0”的个数超过20个时,我就用字典树求解,否则我就暴力求解。结果是200MS过题。
#include<cstdio>
#include<iostream>
#include<cstring>
#pragma comment(linker,"/STACK:102400000,102400000")
using namespace std;
typedef long long ll;
const int maxn=1e6+5;
const int mod=1e9+7;
int Trie[maxn][2]={0};
int tot=1;
ll num[maxn]={0}; //num数组一开始是没有加进去的,结果WA了两遍
char s[1<<11],s0[1<<10][1<<10];
void insert(int len,char* str)
{
int p=0;
for(int i=0;i<len;++i)
{
int id=str[i]-'0';
if(Trie[p][id]==0)
Trie[p][id]=tot++;
p=Trie[p][id];
}
num[p]++;
}
ll query(int len,int rt,int pos)
{
if(pos>=len) return num[rt]; //一开始这里是直接return 1,但是我没有考虑到n个字符串中
//可能会有相等的字符串,所以还要加一个num[i]数组,记录到当前节点i的字符串的个数,然后改为return num[rt]就可以了
if(s[pos]=='_')
{
if(Trie[rt][0]==0)
return query(len,Trie[rt][1],pos+1);
else if(Trie[rt][1]==0)
return query(len,Trie[rt][0],pos+1);
else return query(len,Trie[rt][0],pos+1)+query(len,Trie[rt][1],pos+1);
}
if(Trie[rt][s[pos]-'0']!=0)
return query(len,Trie[rt][s[pos]-'0'],pos+1);
return 0ll;
}
int main()
{
int n,m;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;++i)
{
scanf("%s",s0[i]);
insert(m,s0[i]);
}
int q;
scanf("%d",&q);
getchar();
for(int i=1;i<=q;++i)
{
char c;
int cnt=0,cnt0=0;
while(c=getchar())
{
if(c=='0'||c=='1'||c=='_')
{
s[cnt++]=c;
if(c!='_')
++cnt0;
}
else if(c=='\n') break;
}
if(!cnt0) printf("%d\n",n);
else if(cnt0<=20) //非常曲线的做法,但也是一个小技巧吧
{
int pos[21];
cnt0=0;
for(int i=0;i<cnt;++i)
if(s[i]!='_')
pos[cnt0++]=i;
int ans=0;
for(int i=1;i<=n;++i)
{
bool flag=1;
for(int j=0;j<cnt0;++j)
if(s0[i][pos[j]]!=s[pos[j]])
{
flag=0;
break;
}
if(flag) ++ans;
}
printf("%d\n",ans);
}
else printf("%lld\n",query(m,0,0));
}
return 0;
}