zoukankan      html  css  js  c++  java
  • 子数组的最大异或和&&Trie

    前缀树

    用途:自动补全,拼音检查,ip路由(最长前缀匹配),九宫格打字预测

    还有其他的数据结构,如平衡树和哈希表,使我们能够在字符串数据集中搜索单词。为什么我们还需要 Trie 树呢?尽管哈希表可以在 O(1)O(1) 时间内寻找键值,却无法高效的完成以下操作:

    1. 找到具有同一前缀的全部键值。
    2. 按词典序枚举字符串的数据集。

      Trie 树优于哈希表的另一个理由是,随着哈希表大小增加,会出现大量的冲突,时间复杂度可能增加到 O(n)O(n),其中 nn 是插入的键的数量。与哈希表相比,Trie 树在存储多个具有相同前缀的键时可以使用较少的空间。此时 Trie 树只需要 O(m)O(m) 的时间复杂度,其中 mm 为键长。而在平衡树中查找键值需要 O(m log n)O(mlogn) 时间复杂度。

    Trie 树是一个有根的树,其结点具有以下字段:。

    1. 最多 RR 个指向子结点的链接,其中每个链接对应字母表数据集中的一个字母。
    2. 本文中假定 RR 为 26,小写拉丁字母的数量。
    3. 布尔字段,以指定节点是对应键的结尾还是只是键前缀。
    const int MAXN=26;//英文字符个数
    class Trie
    {
    private:
        Trie *next[MAXN];
        bool isEnd=false;
    public:
        /** Initialize your data structure here. */
        Trie()
        {
            isEnd=false;
            memset(next,0,sizeof(next));
        }
        /** Inserts a word into the trie. */
        void insert(string word)
        {
            if(word.empty())
                return ;
    
            Trie *cur=this;//cur初始化根节点
            for(auto c:word)
            {
                if(cur->next[c-'a']==nullptr)//看当前结点在前缀树中是否存在
                {
                    Trie *node=new Trie();
                    cur->next[c-'a']=node;
                }
                cur=cur->next[c-'a'];//每个结点有个next和isEnd
            }
            cur->isEnd=true;//当前节点已经是一个完整的字符串
            return ;
        }
        /** Returns if the word is in the trie. */
        bool search(string word)
        {
            if(word.empty())
                return false;
    
            Trie *cur=this;
            for(auto c:word)
            {
                if(cur)
                    cur=cur->next[c-'a'];//若c在Trie中不存在,则cur->next[c-'a']为nullptr
            }
            return cur&&cur->isEnd?true:false;//cur不为空且cur指向的结点为一个完整的字符串,则为成功找到
        }
        /** Returns if there is any word in the trie that starts with the given prefix. */
        bool startsWith(string prefix)
        {
            if(prefix.empty())
                return false;
    
            auto cur=this;
            for(auto c:prefix)
            {
                if(cur)
                    cur=cur->next[c-'a'];
            }
            return cur?true:false;
        }
    };

      异或的运算法则为:0⊕0=0,1⊕0=1,0⊕1=1,1⊕1=0(同为0,异为1),这些法则与加法是相同的,只是不带进位,所以异或常被认作不进位加法。

    #include <iostream>
    #include <vector>
    #include <cmath>
    using namespace std;
    
    //1.暴力解O(n^3)
    int get_max_eor(const vector<int>& arr)
    {
        if(arr.empty()||arr.size()<0)
            return -1;
    
        int Max=-0x3f3f;
        for(unsigned int i=0;i<arr.size();++i)//以i结尾的每个子数组
        {
            for(unsigned int start=0;start<=i;++start)//0..i/1..i/2..i等等,前两个for找所有的子数组
            {
                int Eor=0;
                for(unsigned int k=start;k<=i;++k)//遍历每个区间:k=[start..i],求每个区间的最大异或和,arr.at(k)
                    Eor^=(arr.at(k));
                Max=max(Max,Eor);
            }
        }
        return Max;
    }
    
    //2.记忆化优化解O(n^2)
    //0...i异或结果为Sum
    //0...start-1异或结果为A
    //start...i的异或结果为Sum^A
    int get_max_eor1(const vector<int>& arr)
    {
        if(arr.size()<0||arr.empty())
            return -1;
    
        int Eor=0;
        int Max=-0x3f3f;
        for(unsigned int i=0;i<arr.size();++i)
        {
            Eor^=arr.at(i);//遍历0...i,Xor保存每个以i结尾的最长数组的异或和
            Max=max(Max,Eor);//一:可能是整个以i结尾的数组的异或和最大
            int res=0;
            for(unsigned int start=0;start<=i;++start)
            {
                res^=arr.at(start);//每个以i结尾的数组的前半部分
                Max=max(Max,res^Eor);//二:可能是某个子数组的异或和最大
            }
        }
        return Max;
    }
    //2 O(n^2)
    int get_max_eor2(const vector<int>& arr)
    {
        if(arr.size()<0||arr.empty())
            return -1;
    
        vector<int> dp(arr.size(),0);
        int Eor=0;
        int Max=-0x3f3f;
        for(unsigned int i=0;i<arr.size();++i)
        {
            Eor^=arr.at(i);
            Max=max(Max,Eor);
            for(unsigned int start=1;start<=i;++start)
            {
                int res=Eor^dp[start-1];
                Max=max(Max,res);
            }
            dp.at(i)=Eor;
        }
        return Max;
    }
    //3
    //0...i的异或结果Eor存
    //0...0异或结果 0...1的异或结果 0...2的异或结果 直到0...i-1的异或结果(装入黑盒---前缀树)
    typedef struct Node
    {
        Node *next[2];
        Node()
        {
            next[0]=nullptr;
            next[1]=nullptr;
        }
    }Node;
    class Trie
    {
    public:
        int get_max_eor3(const vector<int>& arr);
        ~Trie()
        {
            delete head;
        }
    private:
        int max_eor(const int &num);
        void add(const int &num);
        static Node *head;
    };
    Node* Trie::head=new Node();
    
    int Trie::get_max_eor3(const vector<int>& arr)
    {
        if(arr.size()<0||arr.empty())
            return -1;
    
        int Max=-0x3f3f;
        int Eor=0;
        Trie trie;
        trie.add(0);
    
        for(unsigned int i=0;i<arr.size();++i)
        {
            Eor^=arr.at(i);
            Max=max(max_eor(Eor),Max);
            add(Eor);
        }
        return Max;
    }
    
    void Trie::add(const int& num)//把num添加到前缀树中
    {
        Node *cur=head;
        int res=0;
        for(int move=31;move>=0;--move)
        {
            int path=((num>>move)&1);//取每一位
            cur->next[path]=(cur->next[path]==nullptr?new Node():cur->next[path]);
            cur=cur->next[path];
        }
    }
    //符号位尽量为0,后面的位是0走1,1走0,没得选就将就着走,尽量保持最大化值
    //每次选最优,可以找到最大值
    int Trie::max_eor(const int& num)//num和前缀树中的哪个值异或和最大
    {
        int res=0;
        Node *cur=head;
    
        for(int move=31;move>=0;--move)
        {
            int path=((num>>move)&1);
            int best=(move==31?path:(path^1));
            best=(cur->next[best]!=nullptr?best:(best^1));
            res|=((path^best)<<move);
            cur=cur->next[best];
        }
        return res;
    }
    
    int main()
    {
        vector<int> arr{1,2,3,5,6,8,9};
        cout<<get_max_eor(arr)<<endl;
        cout<<get_max_eor1(arr)<<endl;
        cout<<get_max_eor2(arr)<<endl;
    
        Trie t;
        cout<<t.get_max_eor3(arr)<<endl;
        return 0;
    }

     https://blog.csdn.net/v_july_v/article/details/6685962

  • 相关阅读:
    IOS创建Button简单实例
    IOSActionSheet、AlertView、Slider、Switch的简单使用
    IOS翻转注意
    [教程]iOS 4 开发的好东西 (资料)url
    Proxy和Decorator模式
    Using JNDI connecting DB
    影片“One Day”,还不错
    Minabased TCP server examples
    MySQL vs. Oracle on sequence
    Java DB and JDBC(Embedded Derby)
  • 原文地址:https://www.cnblogs.com/tianzeng/p/10584668.html
Copyright © 2011-2022 走看看