zoukankan      html  css  js  c++  java
  • [模板]前缀树 / 字典树及应用

    前缀树 / 字典树是最简单的树了

    欠的总是要还的

    O(n)算法的多叉树

    比较好理解没啥解释的了

    用数组模拟 行结点 列指针 cnt记录最大结点编号

    直接上代码吧

    可持久化01字典树


    
    struct trie
    {
    	int cnt, root[MAXN], tr[MAXN * 25][2], sum[MAXN * 25];
    //结点总个数 版本进入根        树      结点内个数前缀和  
    	trie() {cnt = 0;}
    	
    	void insert(int now, int last, int val)  //这一版本根下标 上一版本根下标 
    	{
    		int nto, lto;
    		root[now] = nto = ++cnt;
    		lto = root[last];
    		for(int i = 23; i >= 0; --i)
    		{
    			int dig = val >> i & 1;
    			tr[nto][0] = tr[lto][0];
    			tr[nto][1] = tr[lto][1];  //将上一版本儿子直接覆盖 
    			tr[nto][dig] = ++cnt, nto = cnt; //当前版本的新儿子 
    			lto = tr[lto][dig]; 
    			sum[nto] = sum[lto] + 1; //前缀和 + 1 
    		}
    	}
    	
    	int get(int now, int last, int val) //这一版本根下标 上一版本根下标 
    	{
    		int ret = 0;
    		int nto = root[now], lto = root[last];
    		for(int i = 23; i >= 0; --i)
    		{
    			int dig = val >> i & 1 ^ 1;
    			if(sum[ tr[nto][dig] ] - sum[ tr[lto][dig] ]) //当前区间内有该目标节点 
    				ret += (1 << i), nto = tr[nto][dig], lto = tr[lto][dig];
    			else
    				nto = tr[nto][dig ^ 1], lto = tr[lto][dig ^ 1];
    		}
    		return ret;
    	}
    }T;
    
    int arr[MAXN];
    
    int main()
    {  
    	for(int i = 2, t; i <= n; ++i)
    		T.insert(i, i - 1, arr[i]);
    		
    	cout << T.get(y, x - 1, arr[n] ^ val) << '
    ';
    
        return 0;
    }

    1.统计单词数 -> 建树
     

    int trie[MAXN][26] = {0}, cnt = 1, ans = 0;    //trie树 行结点 列指针
    
    bool wend[MAXN] = {false};        //保存以i节点为结尾的单词是否存在
    
    void insert(char word[])
    {
        int len = strlen(word), to = 1;
        
        for(int i = 0; i < len; i++)
        {
            int chn = word[i] - 'a';
            if(!trie[to][chn])
            	trie[to][chn] = ++cnt;
            to = trie[to][chn];
        }
    
        if(!wend[to])    //计和
            ++ans;
    
        wend[to] = true;
    }
    

    2.单词查询 -> 查询是否能在树中完全匹配

    bool wend[MAXN] = {false};        //保存以i节点为结尾的单词是否存在
    
    bool intree(char word[])
    {
        int len = strlen(word), to = 1;
    
        for(int i = 0; i < len; i++)
        {
            int chn = word[i] - 'a';
            if(! trie[to][chn])
                return false;
            to = trie[to][chn];
        }
        return wend[to];  //查询以i节点为结尾的单词是否存在
    }
    

    3.存在前缀 -> 查询是否能在树中前缀匹配

    例题: http://codevs.cn/problem/4189/

    bool intree(char word[]) //只要能扫到单词结尾则必定存在前缀
    {
        int len = strlen(word), to = 1;
    
        for(int i = 0; i < len; i++)
        {
            int chn = word[i] - 'a';
            if(! trie[to][chn])
                return false;
            to = trie[to][chn];
        }
        return true;
    }
    

    4.前缀统计1 -> 此单词为字典中多少单词的前缀的和

    例题: http://acm.hdu.edu.cn/showproblem.php?pid=1251

    int trie[MAXN][26] = {0}, sum[MAXN] = {0}, cnt = 1; //sum数组保存到此结点的前缀总和
    
    bool wend[MAXN] = {false};
    
    void insert(char word[])
    {
        int len = strlen(word), to = 1;
        
        for(int i = 0; i < len; i++)
        {
            int chn = word[i] - 'a';
            if(!trie[to][chn])
            	trie[to][chn] = ++cnt;
            to = trie[to][chn];
            ++sum[to];                         //计和
        }
    
        wend[to] = true;
    }
    
    int precnt(char word[])
    {
        int len = strlen(word), to = 1;
    
        for(int i = 0; i < len; i++)
        {
            int chn = word[i] - 'a';
            if(!trie[to][chn])
                return 0;                //前缀都扫不完查无此单词 和就不用想了
            to = trie[to][chn];
        }
        
        return sum[to];
    }
    

    5.前缀统计2 -> 统计树中有多少单词为当前单词的前缀

    例题:http://acm.ncst.edu.cn/problem.php?pid=1421

    const int MAXN = 1e6 + 10;
    
    int trie[MAXN][26] = {0}, cnt = 1;
    
    int wend[MAXN] = {0}; //wend本用来标记单词结尾, 现在用来标记以该结点结尾的单词的数量
    
    void insert(char word[])
    {
        int len = strlen(word), to = 1;
        
        for(int i = 0; i < len; i++)
        {
            int chn = word[i] - 'a';
            if(!trie[to][chn])
            	trie[to][chn] = ++cnt;
            to = trie[to][chn];
        }
    
        ++wend[to];
    }
    
    int precnt(char word[])
    {
        int len = strlen(word), to = 1, ret = 0;
    
        for(int i = 0; i < len; i++)
        {
            int chn = word[i] - 'a';
            if(!trie[to][chn])
                break;
            to = trie[to][chn];
            ret += wend[to];    //路过的所有结点均为word的前缀 所以加和计算
        }
        
        return ret;
    }
    
    
    
  • 相关阅读:
    AtCoder Grand Contest 030题解
    Codeforces Round #542 (Div. 1) 题解
    ZJOI2019赛季回顾
    UOJ #450「集训队作业2018」复读机
    「IOI2018」狼人
    APIO2019游记
    BZOJ4314 倍数?倍数!
    伯努利数学习笔记&&Luogu P3711 仓鼠的数学题
    Codeforces Round #541 (Div. 2)题解
    UOJ #460 新年的拯救计划
  • 原文地址:https://www.cnblogs.com/zeolim/p/12270396.html
Copyright © 2011-2022 走看看