zoukankan      html  css  js  c++  java
  • trie树

    trie 树,又叫字典树,前缀树,可以用来求查询这个字符串的数量,或者以这个字符串为前缀的字符串的数量。

    trie 树用了一个前缀的思想,就是把每个字符串拆一位位拆开来,然后存在树的边上,比如有 cup,apple,cake,app,blog 这几个单词,那么我们就可以构建这样一棵有根树:

    但是,在树上边是很不好维护的,于是我们可以把他放在这条边的两个端点中,深度较大的那个点上,像这样:

    但这样还没完,我们如何判断这是一个完整字符串呢?比如,你如何知道 ca 是不是一个完整的字符串呢?是判断最后一个字符是否为叶子节点吗?明显不是,上面的例子中,app的最后一个字符就不是叶子节点。那怎么办呢?直接给节点打个标记嘛~
    于是,我们知道了什么是 trie 树,接下来我们要解决插入和查询的问题。
    我们可以开一个数组,用 (trie_{i,j}) 表示节点 i 的儿子中,是字符 j 的节点的编号。当然,我们不能用字符做下标,但是我们可以把他转成数字,比如题目说明只会出现 a~z 的字符,那么我们可以用 x-'a' 来表示字符 x。
    然后,对于插入和查询操作,我们只要枚举字符串的每一位,然后用一个变量 rt 来记录当前节点的编号。然后,对于第 i 个字符,我们只要查询它是否存在,也就是 (trie_{rt,s[i-1]}) 是否为 (0)(字符串下标从 (0) 开始),如果为 (0),若是插入,则加入这个节点;若是查询,则返回 false。
    代码如下:

    void insert(string s)
    {
    	int len=s.length(),root=0;//root即上面的rt
    	for(int i=0;i<len;i++)
    	{
    		int id=s[i]-'a';
    		if(trie[root][id]==0) trie[root][id]=++tot;//tot记录节点数
    		root=trie[root][id];
    	}
    	v[root]=true;//v就是用来记录是否为字符串结尾的、
    	return ;
    }
    bool find(string s)
    {
    	int len=s.length(),root=0;
    	for(int i=0;i<len;i++)
    	{
    		int id=s[i]-'a';
    		if(trie[root][id]==0) return false;
    		root=trie[root][id];
    	}
    	if(v[root]) return true;
            else return false;
    }
    

    然后,上面说到我们可以查询以这个字符串为前缀的字符串有多少,怎么弄呢?我们可以用 (sum_i) 表示有从根节点到 (i) 这个字符串(trie 树上从根节点到一个节点的路径其实就是一个字符串的前缀)前缀的字符串的数量(有点绕、),然后每次插入走到一个节点就 sum[i]++ 即可,而查询则和上面的一样,最后返回末尾节点的 sum 即可。
    代码实现如下:

    int fin_pre(string s)
    {
    	int len=s.length(),root=0;
    	for(int i=0;i<len;i++)
    	{
    		int id=s[i]-'a';
    		if(trie[root][id]==0) return 0;
    		root=trie[root][id];
    	}
    	return sum[root];
    }
    

    整体代码实现如下:

    #include<cstdio>
    #include<string>
    #include<iostream>
    using namespace std;
    int n,m;
    int tot,trie[1000005][26],sum[1000005];
    bool v[1000005];
    void insert(string s)
    {
    	int len=s.length(),root=0;
    	for(int i=0;i<len;i++)
    	{
    		int id=s[i]-'a';
    		if(trie[root][id]==0) trie[root][id]=++tot;
    		root=trie[root][id];
    		sum[root]++;
    	}
    	v[root]=true;
    	return ;
    }
    bool find(string s)
    {
    	int len=s.length(),root=0;
    	for(int i=0;i<len;i++)
    	{
    		int id=s[i]-'a';
    		if(trie[root][id]==0) return false;
    		root=trie[root][id];
    	}
    	if(v[root]) return true;
    	else return false;
    }
    int find_pre(string s)
    {
    	int len=s.length(),root=0;
    	for(int i=0;i<len;i++)
    	{
    		int id=s[i]-'a';
    		if(trie[root][id]==0) return 0;
    		root=trie[root][id];
    	}
    	return sum[root];
    }
    int main()
    {
    	scanf("%d%d",&n,&m);
    	for(int i=1;i<=n;i++)
    	{
    		string s;
    		cin>>s;
    		insert(s);
    	}
    	for(int i=1;i<=m;i++)
    	{
    		int opt;
    		scanf("%d",&opt);//opt=1表示查询这个字符串是否存在,opt=2表示查询包含这个字符串前缀的字符串的数量
    		string s;
    		cin>>s;
    		if(opt==1) printf("%s
    ",find(s)?"Yes":"No");
    		else printf("%d
    ",find_pre(s));
    	}
    	return 0;
    }
    

    由于没有找到合适的模板题所以无法测验,但 insert 和 find 函数是没问题的,find_pre 也不会出太大的问题

  • 相关阅读:
    【SQL】oralce中使用group by和case when按照条件求和
    【SQL】ORACLE在sqlplus中使用spool方式生成建表语句
    【SQL】将特定的元素按照自己所需的位置排序
    【LeetCode刷题】SQL-Second Highest Salary 及扩展以及Oracle中的用法
    【LeetCode刷题】SQL-Combine Two Tables
    CSDN无故封我账号,转战博客园。
    Visual Studio 代码管理器svn插件下载
    Geoserver的跨域问题
    Jmeter+Ant+jenkins实现api自动化测试的持续集成
    python编程中的并发------多线程threading模块
  • 原文地址:https://www.cnblogs.com/mk-oi/p/13629336.html
Copyright © 2011-2022 走看看