【题意】给n个字符串组成的集合,然后有m个询问(0 ≤ n ≤ 3·105, 0 ≤ m ≤ 3·105) ,每个询问都给出一个字符串s,问集合中是否存在一个字符串t,使得s和t长度相同,并且仅有一个字符不同。(字符串总长度为6·105),所有字符只有a,b,c。
【题解】因为只有三种字符,用Trie最合适。先把n个字符串插入到Trie中。然后每读入一个字符串,首先枚举差异字符的位置,依次替换为另外两种字符,并检查是否合法。如果合法就直接输出YES。
显然每替换一个字符就从头检查一遍太浪费时间,所以保存字典树中差异字符位置的指针,每次从该位置开始检查即可。
我一开始是直接处理n个字符串,枚举差异字符的位置,分别替换后加入到字典树中。但是这样做会耗费相当多的空间,空间复杂度是指数级别(3^len)。MLE或者RE。但是一直返回的是WA7,QAQ找了半天错。。
还有一种方法时使用字符串哈希,但是如果运气不好的话,会出现冲突,然后WA掉,这次比赛很多人跪在了这里
#include<bits/stdc++.h> #define eps 1e-9 #define FOR(i,j,k) for(int i=j;i<=k;i++) #define MAXN 1000005 #define MAXM 40005 #define INF 0x3fffffff using namespace std; typedef long long LL; int i,j,k,n,m,x,y,T,ans,big,cas,num,len; bool flag; char s[1000005]; struct Trie { int ch[1000005][26]; int val[1000005]; int size; Trie() { size=1; memset(ch[0],0,sizeof(ch[0])); val[0]=0; val[1]=0; } int idx(char c) { return c-'a'; } void insert(char *s,int v) { int u=0,len=strlen(s); for (int i=0;i<len;i++) { int c=idx(s[i]); if (!ch[u][c]) { memset(ch[size],0,sizeof(ch[size])); val[size]=0; ch[u][c]=size++; } u=ch[u][c]; } val[u]=v; } bool exist(char *s) { int u=0,len=strlen(s); for (int i=0;i<len;i++) { int c=idx(s[i]); for (int j=0;j<3;j++) { if (c==j || ch[u][j]==0) continue; int u2=ch[u][j]; flag=true; for (int k=i+1;k<len;k++) { int c=idx(s[k]); if (!ch[u2][c]) { flag=false; break; } u2=ch[u2][c]; } if (flag && val[u2]) { return true; } } if (!ch[u][c]) { return false; } u=ch[u][c]; } return false; } } tree; int main() { scanf("%d%d",&n,&m); for (i=1;i<=n;i++) { scanf("%s",s); tree.insert(s,1); } for (i=1;i<=m;i++) { scanf("%s",s); if (tree.exist(s)) puts("YES"); else puts("NO"); } return 0; }