https://www.cnblogs.com/dolphin0520/archive/2011/10/12/2208098.html
Ignatius最近遇到一个难题,老师交给他很多单词(只有小写字母组成,不会有重复的单词出现),现在老师要他统计出以某个字符串为前缀的单词数量(单词本身也是自己的前缀).
Input
输入数据的第一部分是一张单词表,每行一个单词,单词的长度不超过10,它们代表的是老师交给Ignatius统计的单词,一个空行代表单词表的结束.第二部分是一连串的提问,每行一个提问,每个提问都是一个字符串.
注意:本题只有一组测试数据,处理到文件结束.
注意:本题只有一组测试数据,处理到文件结束.
Output
对于每个提问,给出以该字符串为前缀的单词的数量.
Sample Input
banana
band
bee
absolute
acm
ba
b
band
abc
Sample Output
2
3
1
0
题意很明确:先给出若干个字符序列{s1,s2,....sn},然后对于字符串s,求算在{s1,s2,s3...sn}中有多少字符序列是以s为前缀的。很容易想到直接将{s1,s2,s3...sn}用数组存储下来,然后逐个匹配,其时间复杂度为O(n*len(s))。经过尝试,严重超时。因此必须采用其它方法,对于公共前缀的匹配,Trie树无疑是不错的选择。因此,采用Trie树去实现,复杂度便下降至O(len(s))。
一道字典树的裸题, 对于单词只有小写字母的情况, 字典树相当于一颗 26 叉树, 每个节点的构成是这样的
struct Trie {
// ARRSIZE个指向孩子节点的指针
Trie* next[ARRSIZE];
int cnt;
Trie() {
for(int i = 0; i < ARRSIZE; i++)
next[i] = 0;
// 注意, 此处是统计以父亲节点为结尾的前缀个数.
cnt = 0;
}
};
主要操作有 插入 和 查询 节点
插入
- 按位寻找字母所代表的下标在next 数组中的值是否为空.若为空, 则创建新的节点.
- 将指针移向下标所指节点, 进行 cnt++ 操作重复第一步操作, 直至遍历完成整个需要插入的字符串;
void insertWord(Trie* root, string str) {
Trie* p = root;
for(int i = 0; i < str.size(); i++) {
int index = (str[i] - 'a');
if(!p->next[index]) {
p->next[index] = new Trie();
}
p = p->next[index];
p->cnt++;
}
}
查询
对于查询前缀操作, 需要注意的一点是 查询的前缀可能根本不存在
代码如下
int searchPrefix(Trie* root, string str) {
Trie* pCrawl = root;
for(int i = 0; i < str.size(); i++) {
int index = str[i] - 'a';
pCrawl = pCrawl->next[index];
//和上一句的顺序很重要
if(pCrawl == NULL)
return 0;
}
return pCrawl->cnt;
}
此处需要特别注意 pCrawl 和 pCrawl->next[index] 的检查方法, 对于样例中的 abc 来说, 由于输入中 abs 是存在 的, 因此指向 b 的 儿子 的是不为空的, 但 pCrwal->next['c' - 'a'] 的值为NULL. 由于 字符c 在串的最后一个, 所以会造成 return NULL->cnt 的错误
struct Trie {
Trie* next[ARRSIZE];
int cnt;
Trie() {
for(int i = 0; i < ARRSIZE; i++)
next[i] = 0;
cnt = 0;
}
};
void insertWord(Trie* root, string str) {
Trie* p = root;
for(int i = 0; i < str.size(); i++) {
int index = (str[i] - 'a');
if(!p->next[index]) {
p->next[index] = new Trie();
}
p = p->next[index];
p->cnt++;
}
}
int searchPrefix(Trie* root, string str) {
Trie* pCrawl = root;
for(int i = 0; i < str.size(); i++) {
int index = str[i] - 'a';
pCrawl = pCrawl->next[index];
//和上一句的顺序很重要
if(pCrawl == NULL)
return 0;
}
return pCrawl->cnt;
}
int main() {
Trie* root = new Trie();
while(true) {
string str; getline(cin, str);
if(str == "")
break;
insertWord(root, str);
}
string str;
while(cin >> str) {
cout << searchPrefix(root, str) << endl;
}
return 0;
}