子数组的最大异或和&&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

posted on 2019-03-23 17:08  tianzeng  阅读(372)  评论(0编辑  收藏  举报

导航