前缀树的节点声明
public static class TrieNode{
public int pass;
public int end;
public TrieNode[] nexts; //一个节点连着那些字符
public TrieNode() {
pass = 0;
end = 0;
nexts = new TrieNode[26]; //定义26条路 a-z
//当字符多种时的时候 可以使用map来进行描述 HashMap<Character, Node> nexts
}
}
定义一个root节点并声明
private TrieNode root;
public Trie() {
root = new TrieNode(); //共用一个root节点
}
插入
public void insert(String word) {
if(word == null) return;
char[] chs = word.toCharArray();
TrieNode node = root;
node.pass++;
int index = 0;
for(int i = 0; i < chs.length; i++) {
index = chs[i] - 'a'; //判断该走那条路
//判断有无路
if(node.nexts[index] == null) {
node.nexts[index] = new TrieNode();
}
node = node.nexts[index]; //移到这条路上
node.pass++;
}
node.end++;
}
查询
//查询加入过几次
public int search(String word) {
if(word == null) return 0;
char[] chs = word.toCharArray();
TrieNode node = root;
int index = 0;
for(int i = 0; i < chs.length; i++) {
index = chs[i] - 'a';
//即查到后面没路了 则返回0 例如之前加入的是abc 现在查询abcd的情况
if(node.nexts[index] == null) return 0;
node = node.nexts[index];
}
return node.end;
}
删除
public void delete(String word) {
if(search(word) != 0) {
char[] chs = word.toCharArray();
TrieNode node = root;
int index = 0;
for(int i = 0; i < chs.length; i++) {
index = chs[i] - 'a';
//如果删除之后pass为0 则置为null
if(--node.nexts[index].pass == 0) {
node.nexts[index] = null;
return;
}
node = node.nexts[index];
}
node.end--;
}
}
主要是参考左神的算法 课程链接
C++实现
#include <iostream>
using namespace std;
const int N = 100010;
//cnt[i] 表示以i节点的单词个数
//idx表示节点的下标 下标为0的既是根节点 又是空节点
int son[N][26], cnt[N], idx;
char str[N];
void insert(char str[])
{
int p = 0;
for(int i = 0; str[i]; i++)
{
int u = str[i] - 'a';
//p表示当前节点 u表示有没有路
if(!son[p][u]) son[p][u] = ++idx; //0为根节点 所以得先++
p = son[p][u]; //转向下一个节点
}
cnt[p]++;
}
int query(char str[])
{
int p = 0;
for(int i = 0; str[i]; i++)
{
int u = str[i] - 'a';
if(!son[p][u]) return 0;
p = son[p][u];
}
return cnt[p];
}
int main()
{
int n;
char op;
cin >> n;
while(n--)
{
cin >> op >> str;
if(op == 'I') insert(str);
else cout << query(str) << endl;
}
return 0;
}
来源于AcwingTrie字符串统计