定义
Trie又称字典树,前缀树,通过下图可以得出Trie的特点:
- Trie的根结点是空的
- 除根结点外,每个结点储存一个字母
- 从根结点到每个字母结点的路径上的所有字母连接而成的字符串就是该结点对应的字符串
- 每个非叶子结点一般都会被多次使用,以节省遍历的时间效率
原理
Trie结点
Trie结点即树上每一个结点,需要维护两个信息:
- 下一个字符指向的结点编号
- 结点附加的值(即当前字符串附加的值)
struct TrieNode {
int val; // 每个字符串附加的值
int next[26]; // 下一个字符的结点编号
} trie[MAXN];
插入操作
当插入一个新的字符串时,从左向右扫描每一个字符,在树中不断向下游走。
void insert(const char* str, int val) {
const char* cur = str;
int node = 0;
while (*cur) {
int ch = *cur - 'a';
int& _next = trie[node].next[ch];
if (!_next) {
++nodes_cnt;
_next = nodes_cnt;
}
node = _next;
++cur;
}
trie[node].val = val;
}
查询操作
查询某个字符串在不在树中,步骤与插入操作类似,返回该字符串对应的结点编号,若不存在则返回0。
int search(const char* str) {
const char* cur = str;
int node = 0;
while (*cur) {
int ch = *cur - 'a';
node = trie[node].next[ch];
if (!node)
return 0;
++cur;
}
return node;
}
例题:正则查询
LeetCode 211
字典树的插入和查询操作,但是查询字符串可以含有正则表达式“.”,匹配任意单个字符。
思路:对于待查询字符串,从左到右,递归地查询,对于“.”,立刻产生26个新的递归分支。注:请留意每个结点维护的valid值,它是必不可少的,表明当前字符串是否存在。如果不使用valid的话,假如插入了“abc”,那么查询“ab”结果也为真。
#include <iostream>
#include <vector>
#include <cstdio>
#include <string>
#include <map>
#include <set>
#include <cstring>
#include <climits>
#include <algorithm>
#include <queue>
#include <stack>
#include <cmath>
#include <cassert>
using namespace std;
const int MAXN = 500005; // MAXN的大小应设为字符串数量×字符串最大长度
struct TrieNode {
bool valid; // 该字符串是否存在
int next[26]; // 下一个字符的结点编号
} trie[MAXN];
int nodes_cnt = 0; // 当前结点数量
void insert(const string& str) {
int node = 0;
for (char _ch : str) {
int ch = _ch - 'a';
int& _next = trie[node].next[ch];
if (!_next) {
++nodes_cnt;
_next = nodes_cnt;
}
node = _next;
}
trie[node].valid = true;
}
bool find(const string& str, int idx, char target, int node) {
if (target == '.') {
for (char ch = 'a'; ch <= 'z'; ++ch) {
if (find(str, idx, ch, node)) {
return true;
}
}
return false;
} else {
int len = str.length();
int ch = target - 'a';
node = trie[node].next[ch];
if (!node)
return false;
if (idx >= len - 1)
return trie[node].valid;
return find(str, idx + 1, str[idx + 1], node);
}
}
class WordDictionary {
public:
/** Initialize your data structure here. */
WordDictionary() {
memset(trie, 0, sizeof(trie));
nodes_cnt = 0;
}
/** Adds a word into the data structure. */
void addWord(string word) {
insert(word);
}
/** Returns if the word is in the data structure. A word could contain the dot character '.' to represent any one letter. */
bool search(string word) {
return find(word, 0, word[0], 0);
}
};