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;
    }
    
    
    
  • 相关阅读:
    Python Revisited Day 13 (正则表达式)
    Python Revisited Day 06 (面向对象程序设计)
    Python Revisited (变量)
    Python Revisited Day 05(模块)
    Python Revisited Day 04 (控制结构与函数)
    Python Revisited Day 03 (组合数据类型)
    Numpy
    Python Revisited Day 01
    Python3使用openpyxl读写Excel文件
    Python3操作YAML文件
  • 原文地址:https://www.cnblogs.com/zeolim/p/12270396.html
Copyright © 2011-2022 走看看