tags:算法
今天在hiro上做了一个题目,要求输入一个字符串,在一个词典中找到以这个字符串开头的字符串有多少个。比如,词典里有abc,ab,adf三个单词,那么以a开头的单词有3个,以ab开头的单词有2个,以ad开头的单词有1个,以abcdefg开头的单词有0个。
就是这样一个规则。
题目中还提示了一个Trie树的概念,然后我去学习了Trie树是长啥样的,搞明白了之后就用C++来写这个题目,C++真的很难用啊。各种奇怪的错误。
Trie树如何存储字符串?
如上图所示,trie树就是一个多叉树,root节点是不存储字符的,从root节点往下,每一层都有各种字符。比如good这个单词,在第一层中放g,第二层中放o,第三层中放o,第四层中放d,如果还有个goose这样的单词,那么上面都是相同的,第四层应该放s,第五层放e。
Trie树中就是这样放数据的,所以,对于英文单词(26个字母)来说,每一个节点下面应该都跟着26个子节点,说实话,这个空间确实占用很大。
Trie树是如何搜索字符串并判断有多少字符串是以关键字开头的?
再画一个图。
假设有
abc
abd
a
adf
四个单词,那么以a开头的有4个,以ab开头的有2个,有ad开头的有一个。
我想的办法是,在读取字符串并插入到trie树中的时候,因为读取字符串后,是挨个挨个的读取每一个字符的,所以当读取abc的时候,先读取a,然后就在第一层的a节点将a赋值给它,并且,让它一个计数器加1,那么也就是说,如果任意一个以a开头的单词,在进行插入树的操作时,都会对第一层的a节点进行一次+1的操作,也就是说,a节点上的数值,表示了以a开头的单词有多少个。
同样的,子节点中,数值的大小,也表示了经过这个节点的次数,比如第二层的b,数值为2,就可以表示以ab开头的单词数为2。
OK,有了以上的方法,我们只需要将他们实现成代码,就可以完成我们需要的功能了,代码如下:
#include<iostream>
#include<string>
using namespace std;
/**
Trie树中的每个节点
*/
class Node {
private:
int count; //计数器,每个单词插入到树中,只要经过这个节点,这个节点就计数一下
char data; //所存储的字符
Node* list; //children
public:
Node() {
count = 0;
data = NULL;
list = NULL;
} //必须要默认的构造函数
Node(char data) {
count = 0;
this->list = new Node[26]; //new是分配一个堆空间,必须手动移除
this->data = data;
}
//设置数据
void setData(char newData) {
data = newData;
count++;
}
//设置子节点的值
void setChild(int index, char data) {
if (list == NULL) {
list = new Node[26];
}
list[index].setData(data);
}
//得到子节点
Node* getChildNode(int index) {
return &list[index];
}
//获取计数
int getCount() {
return count;
}
//获取数据
char getData() {
return data;
}
/*
判断是否是结尾
假如只存储了 abc ,这个单词,但却判断 abcd,如果不做结尾判断,判断到 c 的时候,后面就空指针了
*/
bool isEnd() {
return (list == NULL) ? true : false;
}
~Node() {
delete list;
}
};
/**
Trie树
*/
class Trie {
private:
Node* root;
public:
Trie() {
root = new Node();
root->setData('a'); //随便放一个,让它的数组初始化
}
~Trie() {}
//将一个字符串插入到树中
void insert(string str) {
Node* last = root; //赋个初值,后面循环的时候可以覆盖
for (int i = 0; i < str.length(); i++) { //遍历字符串,挨个挨个的把字符往树的相应位置上方
last->setChild((int)str[i] - 97, str[i]);
last = last->getChildNode((int)str[i] - 97); //重新获取当前操作的节点,因为随着字符串往树的插入不断进行,节点也越来越深
}
}
//在数中搜索一个字符串,返回以这个字符串开头的有多少个单词
int search(string str) {
Node* last = root;
int result = 0;
for (int i = 0; i < str.length(); i++) {
if (last->isEnd()) {
return 0;
}
else {
last = last->getChildNode((int)str[i] - 97)
result = last->getCount();
}
}
return result;
}
};
int main() {
int n = 0; //字典数量
Trie* trieTree = new Trie();
int m = 0; //第二轮输入
cin >> n;
string* dic = new string[n];
for (int i = 0; i < n; i++) {
cin >> dic[i];
trieTree->insert(dic[i]);
}
cin >> m;
string * input = new string[m];
for (int i = 0; i < m; i++) {
cin >> input[i];
}
for (int i = 0; i < m; i++) {
cout << trieTree->search(input[i]) << endl;
}
}
我想每天做一个C++的算法题,来锻炼自己code c++的能力:D