Trie

Trie是指一个字符串集合对应的有根树,树的每条边对应一个字符,根节点表示空串,其他每个节点表示从根到这个节点的路径组成的字符串,在每个属于集合中的字符串的节点处打上标记
Trie是可以高效维护字符串集合,以空间换时间的数据结构,Trie中插入字符串和查询字符串的效率比较高,时间复杂度都是\(O(n)\),其中\(n\)为字符串的长度,但是建立Trie需要一定的空间,如果字符的种类为\(k\),则每个节点的出度都为\(k\)
Trie的结构
数组模拟Trie

const int maxn=500010;
int trie[maxn][30]; //节点i的第j个儿子的节点编号
bool terminal[maxn]; //以某个节点结尾是一个字符串
int tot; //总节点数

插入操作
对于一个待插入的字符串,从Trie的根开始,沿着单词的各个字母所对应的Trie中的节点分支向下走,直到单词遍历完,将最后的节点打上标记,表示已经将单词插入Trie

void insert(char* s){
    int l=strlen(s);
    int root=0;
    for(int i=0;i<l;i++){
        int id=s[i]-'a';
        if(!trie[root][id]) trie[root][id]=++tot;
        root=trie[root][id];
    }
    terminal[root]=true;
}

查询操作
对于一个待查询的字符串,从Trie的根开始按照字符串的字符顺序向下遍历Trie,如果一直可以遍历到字符串结束,并且字符串结尾的节点具有标记,说明查找成功,否则说明某个节点不存在或者字符串末尾节点没有标记,查找失败

bool find(char* s){
    int l=strlen(s);
    int root=0;
    for(int i=0;i<l;i++){
        int id=s[i]-'a';
        if(!trie[root][id]) return false;
        root=trie[root][id];
    }
    return true;
}

Trie结构体

struct Trie{
    struct node{
        int nxt[26];
        int cnt;
        int v;
        void init(){
            memset(nxt,0,sizeof(nxt));
            v=0;
            cnt=0;
        }    
    }t[maxm];
    int root,tot;
    int newnode(){
        ++tot;
        t[tot].init();
        return tot;
    }
    void init(){
        tot=0;
        root=newnode();
    }
    void insert(char *s,LL v) {
        int len=strlen(s);
        int now=root;
        for(int i=0;i<len;i++){
            int id=s[i]-'a';
            if(!t[now].nxt[id]) {
                t[now].nxt[id]=newnode();
            }
            now=t[now].nxt[id];
        }
        t[now].v+=v;
        t[now].cnt++;
    }
    int query(char *s){
        int len=strlen(s);
        int now=root;
        for (int i=0;i<len;i++){
            int id=s[i]-'a';
            now=t[now].nxt[id];
        }
        return t[now].v;
    }
}trie;

模板题
hdu1251 统计难题
统计以某个字符串为前缀的字符串个数
hdu2072 单词数
统计一些字符串中不同字符串的种类

posted @ 2020-08-09 16:12  fxq1304  阅读(87)  评论(0)    收藏  举报