题意
给定很多个单词后, 统计以某个字符串为前缀的单词数量(单词本身也是自己的前缀).
题解
一道字典树的裸题, 对于单词只有小写字母的情况, 字典树相当于一颗 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 的错误
AC 代码
#include <cstdio>
#include <string>
#include <iostream>
#include <cstring>
using namespace std;
const int ARRSIZE = 26;
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;
}