C++实现Trie字典树

一、前言

这篇文章来源于HihoCode的#1014题,此处为链接

二、Trie字典树

字典树,顾名思义就是将单词存在一个树结构中,用来对单词进行存储和查找功能,并且可以统计出以某一特定字符串为前缀共有多少单词,本文中只涉及英文单词。

字典树是一个26叉树,每一个父节点都有26个子节点,分别对应了26个英文字母,树的结构大致如下:

理所当然的根节点不代表任何字母,作为一个空节点。那么每个节点应当具有什么样的结构呢?

三、节点结构

节点中应当包含一个指向26个子节点的指针数组,为了统计以某一字符串为前缀的单词出现了多少次,节点中应当有此节点存储了多少次的计数值,并且应当存储下当前节点代表的字符。

class TrieNode{
    public:

    int count;
    TrieNode * nextNode[26];
    char *word;
    public:
    TrieNode():word(NULL),count(0){
        for(int i=0;i<26;i++){
            nextNode[i]=NULL;
        }
    }

};
四、针对题目的分析

题目中要求先将给出的单词存入树中,这就要求我们的树需要有插入操作。插入操作可以通过递归和循环来实现,在这里我们介绍循环的方式。

每次插入一个单词时我们需要将对应的字符串的每一位提取出来,判断这个字符是否为合法的英文字符,并且在当前节点的子节点中找到这个字符对应的子节点。如果这个子节点不为空,说明之前已经插入过一次,那么这次插入只需要更新它的count计数,否则的话我们需要初始化这个节点,并且将它对应的字符和count计数值都更新。

void insert(string str){
        int index;
        TrieNode *tmp=root;
        tmp->count++;
		for(int i=0;i<str.size();i++){
            index=str[i]-'a';
            if(index < 0 || index > 25) return;
            else if(tmp->nextNode[index]==NULL){
                tmp->nextNode[index]=new TrieNode();


                //tmp->nextNode[index]->word
            }

            tmp=tmp->nextNode[index];
            tmp->count++;
            tmp->word[0]=str[i];
        }

    }
当所有的单词都插入后,我们需要对给定的前缀出现的次数进行统计,也就是我们要找到这个前缀的最后一个字符所在的节点的count计数值。因此从根节点开始层层寻找,如果没有到这个前缀的字符的最后一位就遇到了空节点,说明字典中并没有存放这个前缀单词,需要返回零。如果找到了最后一个字符所在的节点,那么就返回它的计数值。

bool isFound(const string str,int & cnt){
        int i=0;
        int index=-1;
        TrieNode *tmp=root;
        for(;i<str.size();i++){
            index=str[i]-'a';
			if(0>index || index>25) {cnt=0;return false;}

            tmp=tmp->nextNode[index];
            if(tmp!=NULL) cnt=tmp->count;
			else    {cnt=0;return false;}
        }

        return true;
    }
这两个方法已经足够我们应付这道题目了,下面来介绍一下方便我们调试用的一个函数——打印整棵树。

打印整棵树其实就是遍历整棵树,我们为了方便就采取前序遍历,遍历采用简单的递归。

void print(TrieNode * tmp){
		if(tmp==NULL) return;
		else{
			cout<<tmp->word[0]<<"\t";
		}
		for(int i=0;i<26;i++)
			print(tmp->nextNode[i]);
	}
	void printAll(){
		print(root);
	}
五、完整代码

这里贴出完整的代码供大家参考。

#include <iostream>
#include <string>

using namespace std;


class TrieNode{
    public:

    int count;
    TrieNode * nextNode[26];
    char *word;
    public:
    TrieNode():word(NULL),count(0){
        for(int i=0;i<26;i++){
            nextNode[i]=NULL;
        }
    }

};


class TrieTree{
    public:
    TrieNode * root;
    TrieTree(){
        root=new TrieNode();
    }
    ~TrieTree(){
        delete [] root;
    }
    void insert(string str){
        int index;
        TrieNode *tmp=root;
        tmp->count++;
		for(int i=0;i<str.size();i++){
            index=str[i]-'a';
            if(index < 0 || index > 25) return;
            else if(tmp->nextNode[index]==NULL){
                tmp->nextNode[index]=new TrieNode();


                //tmp->nextNode[index]->word
            }

            tmp=tmp->nextNode[index];
            tmp->count++;
            tmp->word[0]=str[i];
        }

    }

    bool isFound(const string str,int & cnt){
        int i=0;
        int index=-1;
        TrieNode *tmp=root;
        for(;i<str.size();i++){
            index=str[i]-'a';
			if(0>index || index>25) {cnt=0;return false;}

            tmp=tmp->nextNode[index];
            if(tmp!=NULL) cnt=tmp->count;
			else    {cnt=0;return false;}
        }

        return true;
    }

	void print(TrieNode * tmp){
		if(tmp==NULL) return;
		else{
			cout<<tmp->word[0]<<"\t";
		}
		for(int i=0;i<26;i++)
			print(tmp->nextNode[i]);
	}
	void printAll(){
		print(root);
	}
};
int N;
int S;
int main(){
    TrieTree t;
    int cnt=0;
    string str;
    cin>>N;
    int i;
	int *a;
    for(i=0;i<N;i++){
        cin>>str;
        //cout<<"hi"<<endl;
        t.insert(str);
    }
	t.printAll();
    cin>>S;
	a=new int[S];
    for(i=0;i<S;i++){
        cnt=0;
        cin>>str;
        t.isFound(str,cnt);
        a[i]=cnt;
    }

	for(i=0;i<S;i++){
		cout<<a[i]<<endl;
	}
    return 0;
}




  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
下面是用 C++ 代码实现字典树Trie)的模糊搜索: ```c++ #include <iostream> #include <vector> #include <string> #include <cstring> using namespace std; const int MAX_N = 1000005; const int MAX_M = 10005; const int MAX_L = 25; struct TrieNode { int son[26]; bool is_end; } trie[MAX_N]; int tot = 1; void insert(string s) { int p = 1; for (int i = 0; i < s.length(); i++) { int c = s[i] - 'a'; if (!trie[p].son[c]) { trie[p].son[c] = ++tot; } p = trie[p].son[c]; } trie[p].is_end = true; } vector<string> ans; void dfs(int p, string s, int cnt, int k) { if (trie[p].is_end && cnt <= k) { ans.push_back(s); } if (cnt > k) { return; } for (int i = 0; i < 26; i++) { if (trie[p].son[i]) { char c = 'a' + i; dfs(trie[p].son[i], s + c, cnt + (c != s[s.length() - 1]), k); } } } int main() { int n, m, k; cin >> n >> m >> k; for (int i = 1; i <= n; i++) { string s; cin >> s; insert(s); } for (int i = 1; i <= m; i++) { string s; cin >> s; ans.clear(); dfs(1, "", 0, k); int cnt = 0; for (int j = 0; j < ans.size(); j++) { if (ans[j].find(s) != string::npos) { cnt++; } } cout << cnt << endl; } return 0; } ``` 在这个代码中,`trie` 数组表示字典树,`tot` 表示字典树的节点数量。`insert()` 函数用于将单词插入字典树。`dfs()` 函数用于进行深度优先搜索,其中 `p` 表示当前节点的编号,`s` 表示当前字符串,`cnt` 表示当前编辑距离,`k` 表示最大编辑距离。在 `dfs()` 函数中,如果当前节点是一个单词的结尾,并且编辑距离不超过 `k`,则将当前字符串加入答案数组 `ans` 中。然后遍历当前节点的所有儿子节点,如果某个儿子节点存在,则继续递归搜索。在递归搜索时需要注意,如果当前字符和上一个字符相同,则不需要增加编辑距离,否则需要增加编辑距离。 在主函数中,首先读入字典树中单词的数量 `n`,查询的数量 `m`,以及最大编辑距离 `k`。然后逐个读入查询字符串,对于每个查询字符串,先清空答案数组 `ans`,然后调用 `dfs()` 函数进行搜索。搜索完成后,遍历答案数组 `ans`,如果某个字符串中包含查询字符串,则将计数器 `cnt` 加一。最后输出计数器 `cnt` 即可。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值