字典树原理及应用

概要

  • 字典树又叫前缀树,一般用来存储一些单词或数字,在查找的时候比较方便,由于相同前缀只存储一次,所以也是比较省空间的
  • 字典树是一棵树,一般用数组存储比较方便,从根节点出发,根节点不存储字母,每遇到一个字母,先判断树上是不是已经有了,如果有了,就顺着这条路;如果没有,新建立一条路径,直到最后一个字母,这时候将末尾字母所在位置染色,表示这是末尾字母。所以,字典树是一棵多叉树。
    在这里插入图片描述
    字典树思想相对比较容易

插入操作

  • 使用p作为索引,贯穿于整条链,用k来记录节点次序,因为根节点是不存字母的,所以最后染色位置就是最后一个字母位置
void insert(string s){
    
    
    int len = s.length();
    int p = 0;
    for(int i=0;i<len;i++){
    
    
        char c = s[i] - 'a';
        if(!Trie[p][c]){
    
    
            Trie[p][c] = k++;
        }
        p = Trie[p][c];
    }
    color[p] = 1;
}

查找操作

  • 查找一个单词是否在字典树中,只要找到一条和单词吻合的链,且它的末尾被染色即可
bool search(string s){
    
    
    int len = s.length();
    int p = 0;
    for(int i=0;i<len;i++){
    
    
        char c = s[i] - 'a';
        if(!Trie[p][c]) return false;
        p = Trie[p][c];
    }
    return color[p] == 1;
}

例题

洛谷P2580

  • 此题完全可用map求解,非常简单,但是此题可作为字典树第一道习题
  • 第几次出现,则字典树相应单词末尾染不同的颜色即可
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <string>
#include <cmath>
#include <iomanip>
#include <queue>
#include <stack>
using namespace std;
typedef long long ll;
const int MAXN = 2e6+100;
int Trie[MAXN][30];
int color[MAXN];
int k = 1;
void insert(string s){
    
    
    int len = s.length();
    int p = 0;
    for(int i=0;i<len;i++){
    
    
        char c = s[i] - 'a';
        if(!Trie[p][c]){
    
    
            Trie[p][c] = k++;
        }
        p = Trie[p][c];
    }
    color[p] = 1;
}
string search(string s){
    
    
    int len = s.length();
    int p = 0;
    for(int i=0;i<len;i++){
    
    
        char c = s[i] - 'a';
        if(!Trie[p][c]) return "WRONG";
        p = Trie[p][c];
    }
    if(color[p] == 1){
    
    
        color[p] = 2;
        return "OK";
    }else return "REPEAT";
}
int main(){
    
    
    int n,m;
    cin>>n;
    string s;
    for(int i=0;i<n;i++){
    
    
        cin>>s;
        insert(s);
    }
    cin>>m;
    for(int i=0;i<m;i++){
    
    
        cin>>s;
        cout<<search(s)<<endl;
    }
    return 0;
}

hdu1251

  • 问以某个单词为前缀的单词数量。使用一个number数组记录单词前缀数量,每次加入一个单词,更新number数组即可
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <string>
#include <cmath>
#include <iomanip>
#include <queue>
#include <stack>
using namespace std;
typedef long long ll;
const int MAXN = 2e6+100;
int Trie[MAXN][30];
int k = 1;
int color[MAXN]; 
int number[MAXN];
void insert(string s){
    
    
    int len = s.length();
    int p = 0;
    for(int i=0;i<len;i++){
    
    
        int c = s[i] - 'a';
        if(!Trie[p][c]){
    
    
            Trie[p][c] = k;
            k++;
        }
        number[p] ++;
        p = Trie[p][c];
    }
    number[p]++;
}
int search(string s){
    
    
    int p = 0;
    int len = s.length();
    for(int i=0;i<len;i++){
    
    
        int c = s[i] - 'a';
        if(!Trie[p][c]) return 0;
        p = Trie[p][c];
    }
    return number[p];
}
int main(){
    
    
    string s;
    while(true){
    
    
        getline(cin,s);
        if(s == "") break;
        insert(s);
    }
    while(cin>>s){
    
    
        cout<<search(s)<<endl;
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/roadtohacker/article/details/112855326