Trie(字典树)
#include<bits/stdc++.h>
using namespace std;
const int N =1000*1000+10;
int n,m,size[N][26],tot=1,endpos[N*2];
char s[N];
void insert(char s[]){
int len=strlen(s),p=1;
for(int i=0;i<len;i++){
int a=s[i]-'a';
if(size[p][a]==0)size[p][a]=++tot;
p=size[p][a];
}
endpos[p]++;
}
int solve(char s[]){
int ans=0;
int len=strlen(s);
int p=1;
for(int i=0;i<len;i++){
int k=s[i]-'a';
int f=size[p][k];
if(endpos[f])ans+=endpos[f];
if(f==0)break;
p=size[p][k];
}
return ans;
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++){
scanf("%s",s);
insert(s);
}
for(int i=1;i<=m;i++){
scanf("%s",s);
printf("%d\n",solve(s));
}
return 0;
}
可持久化Trie
保存的是前k个串的信息,如果想要查询区间[L,R]的信息,可以在节点上增加一个信息,表示当前节点最后一次被哪一个串使用last[],查询的时候从R开始查询,每查询的时候都要满足last[p]>=L,才可以走这条路去查询。
#include<bits/stdc++.h>
using namespace std;
const int N = 3 * 100 * 1000 + 10;
char s[N];
int rt[N], trie[N][26],endpos[N],last[N];
int tot = 1;
int len=0;
void insert(int &now, int pre, int p,int k) { //当前节点,上次访问的节点,访问到第p个字符,第k个字符串
now = ++tot;
rt[now] = rt[pre];
if (p == len) {
endpos[now] = 1; //标记字符串的结束节点
last[now] = k; //当前节点最后一次被第k个字符串使用
return;
}
int pos = s[p] - 'a';
if (pre)for (int i = 0; i < 26; i++)trie[now][i] = trie[pre][i]; //复制儿子节点的所有信息
insert(trie[now][pos], trie[pre][pos], p + 1,k);
for (int i = 0; i < 26; i++)last[now] = max(last[now],last[trie[now][i]]);//更新当前节点最后一次被使用的位置
}
void dfs(int k,int r,vector<char>v) {
if (endpos[r]) {
for (int i = 0; i < v.size(); i++)cout << v[i];
cout << endl;
return;
}
for (int i = 0; i < 26; i++) {
if (trie[r][i]&&last[trie[r][i]]>=k) {
//printf("%c",i+'a');
vector<char> vv = v;
vv.push_back('a'+i);
dfs(k,trie[r][i],vv);
}
}
}
int main() {
int cnt = 0;
while (scanf("%s", s), s[0] != '#') {
len = strlen(s);
insert(rt[++cnt], rt[cnt - 1], 0,cnt);
}
vector<char>v;
dfs(2,rt[3],v);
return 0;
}
变种:01字典树,一般是解决异或问题,就是对每个数在二进制表示下从最高位开始建树,查询的时候尽量往相反方向走。这里就不给出代码了,和上面的类似。