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字典樹,一般是解決異或問題,就是對每個數在二進制表示下從最高位開始建樹,查詢的時候儘量往相反方向走。這裏就不給出代碼了,和上面的類似。