版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/zhaohaibo_/article/details/83119696
Description
[字典树模版题] 给定N个字符串S1,S2…SN,接下来进行M次询问,每次询问给定一个字符串T,求S1~SN中有多少个字符串是T的前缀。输入字符串的总长度不超过10^6,仅包含小写字母。字符串 S1(不妨假设长度为 n)被称为字符串 S2 的前缀,当且仅当:S2 的长度不小于 n,且 S1 与 S2 前 n 个字符组组成的字符串完全相同。
Input
第一行两个整数N,M。接下来N行每行一个字符串Si。接下来M行每行一个字符串表示询问。
Output
对于每个询问,输出一个整数表示答案
Sample Input
3 2
ab
bc
abc
abc
efg
Sample Output
2
0
把这N个字符串插入一颗Trie树中,Trie的每个节点上存储一个整数cnt,记录该节点是多少个字符串的末尾节点。(为了处理插入重复字符串的情况,这里要记录个数,而不能只做结尾标记)
对于每个询问,在Trie树中检索T,在检索过程中累加途径的每个节点的cnt值,就是该询问的答案。
关于Trie树:本题需要插入和检索的字符串都由小写字母构成,所以Trie每个节点具有26个字符指针分别为a到z。
在Trie树中,字符数据都体现在树的边(指针)上,树的节点仅保存一些额外信息,例如单词结尾标记等。其空间复杂度为O(NC),其中N是节点个数,C是字符集的大小。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int SIZE=1000010;
// trie每个节点记录cnt, 根节点为1
int trie[SIZE][26], cnt = 1;
// 存在重复字符串
int edTot[SIZE];
int n, m;
char str[SIZE];
// 向Trie中插入一个字符串
void insert(char* str) {
// 当需要插入一个字符串str时,令一个指针p起初指向根节点。然后,依次扫描str中的每个字符c。
int p = 1;
// 一个一个单词插入
for (int k = 0; k < strlen(str); k++) {
int ch = str[k]-'a';
// 如果p的ch字符指针指向空,则新建一个节点Q,令p的c字符指针指向Q
if (!trie[p][ch])
trie[p][ch] = ++cnt;
// 令P = Q
p = trie[p][ch];
}
edTot[p]++;
}
int search(char* str) {
int p = 1;
int ans = 0;
for (int k = 0; k < strlen(str); k++) {
p = trie[p][str[k]-'a'];
if (p == 0) return ans;
ans += edTot[p];
}
return ans;
}
int main() {
cin>>n>>m;
for(int i=1;i<=n;i++) {
scanf("%s",str);
insert(str);
}
for(int i=1;i<=m;i++) {
scanf("%s",str);
printf("%d\n", search(str));
}
}