zoukankan      html  css  js  c++  java
  • 【字典树(我的字典我做主)】

    一、Tried的概念

      又称单词查找树,Trie树,是一种树形结构,是一种哈希树的变种。典型应用是用于统计,排序和保存大量的字符串(但不仅限于字符串),所以经常被搜索引擎系统用于文本词频统计。它的优点是:利用字符串的公共前缀来减少查询时间,最大限度地减少无谓的字符串比较,查询效率比哈希树高。

    来了解一下存储方式:

      此时我们也了解了一些字典树的特点

        1、根节点不包含字符。

        2、除根节点外每一个节点都只包含一个字符。

        3、从根节点到某一节点,路径上经过的字符连接起来,为该节点对应的字符串; 每个节点的所有子节点包含的字符都不相同。

    二、Trie的操作

      常见的操作有插入、查找、删除。(由于博客主还没有学删除操作,所以.....

      在这棵树中,我们会用一个数组来存每一个节点的编号,根节点编号一般为0。

      然后,ch[i][j]表示i的编号为j的子节点,所以当ch[i][j]=0时节点不存在。

      最后还有个val数组,是专门用来存附加条件的。

    必要元素

    我们需要val数组来记录比较重要的数值(例如某个单词在x页,在单词结尾记录x,或一个词有,记录为1)

    ch数组来建树,一个点连向几个点,方便后面查找操作。

    sz记录编号

    int ch[N][26];
    int val[N];
    int sz;

    初始化

    注意,ch数组没必要全部初始化,会浪费很多时间,当必要的时候再初始化。

    void clean()
        {
            sz=1;
            memset(ch[0],0,sizeof ch[0]);
            memset(val,0,sizeof(val));
        }

    插入

    插入就是从字符串的第一位开始遍历一直到最后,已经有的节点就利用一下(利用前缀节省时间)然后没有的时候就新开一个

    (这里的get就是s[i]-'a',当然,不一定非要是a,按照题目变化)

    void insert(char s[],int,sum)
        {
            int u=0;//根节点是0
            for(int i=0;i<strlen(s);i++)//遍历
            {
                int v=get(s[i]);//下一个点
                if(!ch[u][v])//没有开,就开一下,初始化
                {
                    memset(ch[sz],0,sizeof(ch[sz]));
                    ch[u][v]=sz;
                    sz++;
                }
                u=ch[u][v];//继续搜
            }
            val[u]=sum;//在末尾标记
        }

    查找

    和插入差不多,但是当没有下一个点的时候直接返回没有找到。最后到结尾判断一下是否是字符串(万一是字串也会一路找下来)

    int find(char s[])
        {
            int u=0;
            for(int i=0;i<strlen(s);i++)
            {
                int v=get(s[i]);
                if(!ch[u][v])return 0;
                u=ch[u][v];
            }
            if(!val[u])return 0;
            return 1;
        }

    luogu P2580 于是他错误的点名开始了掌握了以上操作的可以看看,因为删除操作一般不是很常用

     删除

    当一道题需要用到删除的时候,一般都会想到直接删除末尾的标记,但是这样做的话有一个弊端

     假设我们删除abcd

     但是当我们下一次查找的时候,还是会按照abcd的顺序查找,当数据极端,就会造成时间浪费。所以我们要新开一个数组,cnt[u]表示节点u被经过的次数

     当我们删除的时候,把沿途的cnt[u]减一,当有一个节点的经过数为0的时候,说明这个节点没有搜索的必要,就会节省很多时间

     当我们想要搜索abcd是,搜索到b就会返回,而不会傻傻的搜到底部(当然这个只针对一定可以删除到的操作)

    void delet(char s[])
        {
            int u=0;
            for(int i=0;i<strlen(s);i++)
            {
                int v=get(s[i]);
                u=ch[u][v];
                cnt[u]--;
            }
            val[u]=0;
        }

    因为多了个cnt数组,所以插入的途中也要cnt[u]++

    HDU5526这道题就是,因为防止搜索到与a[i],a[j]相同的数组,所以我们每次要从字典树里删除掉它,然后再加回去

    #include<bits/stdc++.h>
    using namespace std;
    const int N=100010;
    bitset<32>s;
    int n,m,ans;
    int a[N];
    struct Trie{
        int ch[N][2];
        int cnt[N];
        int val[N];
        int sz;
        void clean(){
            memset(ch[0],0,sizeof ch[0]);
            memset(val,0,sizeof(val));
            memset(cnt,0,sizeof cnt);
            sz=1;
        }
        int get(char a){return a;}
        void insert(int sum)
        {
            int u=0;
            for(int i=31;i>=0;i--)
            {
                int v=get(s[i]);
                if(!ch[u][v])
                {
                    memset(ch[sz],0,sizeof ch[sz]);
                    ch[u][v]=sz;
                    sz++;
                }
                u=ch[u][v];
                cnt[u]++;
            }
            val[u]=sum;
        }
        void delet()
        {
            int u=0;
            for(int i=31;i>=0;i--)
            {
                int v=get(s[i]);
                u=ch[u][v];
                cnt[u]--;
            }
        }
        int find()
        {
            int u=0;
            for(int i=31;i>=0;i--)
            {
                int v=get(s[i]);
                if(!cnt[ch[u][(v+1)%2]])u=ch[u][v];
                else u=ch[u][(v+1)%2];
            }
            return val[u];
        }
    }T;
    int main()
    {
        int t;
        scanf("%d",&t);
        while(t--)
        {
            T.clean();
            ans=0;
            scanf("%d",&n);
            for(int i=1;i<=n;i++)
            {
                scanf("%d",&a[i]);
                s=a[i];
                T.insert(i);
            }
            for(int i=1;i<=n;i++)
            {
                s=a[i];
                T.delet();
                for(int j=i+1;j<=n;j++)
                {
                    s=a[j];
                    T.delet();
                    s=a[i]+a[j];
                    ans=max(ans,(a[i]+a[j])^a[T.find()]);
                    s=a[j];
                    T.insert(j);
                }
                s=a[i];
                T.insert(i);
            }
            printf("%d
    ",ans);
        }
        
        return 0;
    }
  • 相关阅读:
    Docker | 第二章:第一个Docker应用
    Docker | 第一章:Docker简介
    Docker | 第零章:前言
    SpringBoot | 第十五章:基于Postman的RESTful接口测试
    SpringBoot | 第十四章:基于Docker的简单部署
    SpringBoot | 第十三章:测试相关(单元测试、性能测试)
    2014年计划
    学习方法,工作方法 探讨
    2013年总结
    asp.net用Zxing库实现条形码输出
  • 原文地址:https://www.cnblogs.com/hualian/p/11200429.html
Copyright © 2011-2022 走看看