1803. 统计异或值在范围内的数对有多少

news2024/9/20 19:37:38

解法一:字典树

前置知识:字典树


字典树是一种实现字符串快速检索的多叉树结构。

例如:给定字符串集合[cab, cos, car, cat], 我们现在需要判断cat是否存在于字符串集合中。
在这里插入图片描述字典树代码:

static int[][] trie = new int[N][26]; //其中N为结点个数,一般为所有字符串长度之和。
static int[] cnt = new int[N]; //代表红色结点结尾出现的次数
void add(String str) { //将字符串str添加进字典树
        int p = 0; //根结点为0号
        for (char c : str.toCharArray()) {
            int u = c - 'a';
            if (trie[p][u] == 0) trie[p][u] = ++idx; //创建结点并赋予编号idx
            p = trie[p][u]; //走到下一个结点
        }
        cnt[p]++; //个数增加
    }
    static int query(String str) {
        int p = 0;
        for (char c : str.toCharArray()) {
            int u = c - 'a';
            if (trie[p][u] == 0) return 0; //若当前结点不存在,那么直接返回0
            p = trie[p][u];
        }
        return cnt[p];  
    }
int trie[N][26], cnt[N], idx;
void insert(string str){
    int p = 0;
    for (int i = 0; i < str.length(); i++){
        int u = str[i] - 'a';
        if (!trie[p][u]) trie[p][u] = ++ idx;//创建结点并赋予编号idx
        p = trie[p][u];//走到下一个结点
    }
    cnt[p]++ ;//个数增加
}

int query(string str){
    int p = 0;
    for (int i = 0; i < str.length(); i++){
        int u = str[i] - 'a';
        if (!trie[p][u]) return 0; //若当前结点不存在,那么直接返回0
        p = trie[p][u];
    }
    return cnt[p];//返回存在的次数
}

动态开辟结点:

class Trie {
     Trie[] trie = new Trie[26];
     int cnt;
}
Trie root = new Trie();
void add(String str) {
    Trie p = root;
    for (char c : str.toCharArray()) {
        int u = c - 'a';
        if (p.trie[u] == null)  p.trie[u] = new Trie(); //创建结点
        p = p.trie[u];
    }
    p.cnt++; //个数增加
}
int query(String str) {
    Trie p = root;
    for (char c : str.toCharArray()) {
        int u = c - 'a';
        if (p.trie[u] == null) return 0;
        p = p.trie[u];
    }
    return p.cnt;
}
  • 相关题目:208. 实现 Trie (前缀树) 421. 数组中两个数的最大异或值,建议不熟悉字典树的先将这两道题目做一下。

回到本题,我们要求 ( i , j ) (i, j) (i,j) 的数对使得 l o w < = ( n u m s [ i ]   X O R   n u m s [ j ] ) < = h i g h low <= (nums[i]\ XOR\ nums[j]) <= high low<=(nums[i] XOR nums[j])<=high。首先对于每个数字,我们可以通过二进制来表示,由于 n u m s [ i ] ≤ 2 ∗ 1 0 4 nums[i]\leq 2 * 10^4 nums[i]2104,因此15位二进制就可以进行表示。对于某个数x = 3,二进制表示“000000000000011”, 我们将该串存入字典树中。


题目需要求异或值在 [ l o w . h i g h ] [low. high] [low.high]之间的数,直接求解不太好求解,我们可以通过容斥原理转化一下,求解 a n s   =   ≤ h i g h ans\ =\ \leq high ans = high的对数 - ≤ ( l o w − 1 ) \leq (low-1) (low1)的对数。


如何在字典树中求解 ( i , j ) (i,j) (i,j)对的异或值小于等于 h i g h high high?

首先, i < j i < j i<j, 对于某个j来说,我们将之前 [ 1 , j − 1 ] [1,j-1] [1,j1]的数存入字典树中,在实现 a d d ( ) add() add()方法时,我们对每一个结点都统计其出现的位置,方便我们后面计算个数。当 [ 1 , j − 1 ] [1,j-1] [1,j1]的数都添加进字典树后,我们进行一次查询 q u e r y ( ) query() query()返回 ≤ h i g h \leq high high的异或对数量。


在这里插入图片描述

  • 时间复杂度: O ( n l o g m ) O(nlogm) O(nlogm), 其中m为最大的数
  • 空间复杂度: O ( n l o g m ) O(nlogm) O(nlogm),开辟结点数量
class Solution {
    int[][] trie;
    int[] cnt;
    int idx;
    public int countPairs(int[] nums, int low, int high) {
        trie = new int[nums.length * 16][2];
        cnt = new int[nums.length * 16];
        return get(nums, high) - get(nums, low - 1);
    }
    int get(int[] nums, int high) {
        idx = 0;
        for (int i = 0; i < trie.length; i++) trie[i][0] = trie[i][1] = cnt[i] = 0;
        int ans = 0;
        for (int i = 0; i < nums.length; i++) {
            ans += query(nums[i], high);
            add(nums[i]); 
        }
        return ans;
    }
    void add(int x) {
        int p = 0;
        for (int i = 14; i >= 0; i--) {
            int u = (x >> i)  & 1;
            if (trie[p][u] == 0) trie[p][u] = ++idx;
            p = trie[p][u]; //移动到下一个结点 
            cnt[p]++; // 个数增加,cnt[x]代表x结点出现的次数
        }
    }
    int query(int x, int high) {
        int sum = 0, p = 0;
        for  (int i = 14; i >= 0; i--) {
            int u = (x >> i) & 1;
            if (((high >> i) & 1) == 1) { //high当前i位为1, 那么x与以前数当前i位的异或可以位1或者0
                sum += cnt[trie[p][u]];//加上与x异或后当前i位为0的数量
                if (trie[p][u ^ 1] == 0) return sum; //没有结点可以继续走下去,直接返回
                p = trie[p][u ^ 1]; //继续往异或的结点走下去
            } else { //high当前i位为0, x与以前数异或的第i为必须为0
                if (trie[p][u] == 0) return sum; //没有结点走下去
                p = trie[p][u]; //寻找与x的第i位相同的进制,异或结果为0
            }
        }
        sum += cnt[p]; //加上走到最后的结点数
        return sum;
    }
}
const int N = 20005;
int trie[N * 15][2], cnt[N * 15], idx;
class Solution { 
public:
    int countPairs(vector<int>& nums, int low, int high) {
        return get(nums, high) - get(nums, low - 1);
    }
    int get(vector<int>& nums, int high) {
        idx = 0;
        memset(trie, 0, sizeof(trie));
        memset(cnt, 0, sizeof(cnt));
        int ans = 0;
        for (int i = 0; i < nums.size(); i++) {
            ans += query(nums[i], high);
            add(nums[i]); 
        }
        return ans;
    }
    void add(int x) {
        int p = 0;
        for (int i = 14; i >= 0; i--) {
            int u = (x >> i)  & 1;
            if (trie[p][u] == 0) trie[p][u] = ++idx;
            p = trie[p][u]; //移动到下一个结点 
            cnt[p]++; // 个数增加,cnt[x]代表x结点出现的次数
        }
    }
    int query(int x, int high) {
        int sum = 0, p = 0;
        for  (int i = 14; i >= 0; i--) {
            int u = (x >> i) & 1;
            if (((high >> i) & 1) == 1) { //high当前i位为1, 那么x与以前数当前i位的异或可以位1或者0
                sum += cnt[trie[p][u]];//加上与x异或后当前i位为0的数量
                if (trie[p][u ^ 1] == 0) return sum; //没有结点可以继续走下去,直接返回
                p = trie[p][u ^ 1]; //继续往异或的结点走下去
            } else { //high当前i位为0, x与以前数异或的第i为必须为0
                if (trie[p][u] == 0) return sum; //没有结点走下去
                p = trie[p][u]; //寻找与x的第i位相同的进制,异或结果为0
            }
        }
        sum += cnt[p]; //加上走到最后的结点数
        return sum;
    }
};
  • 动态开点
class Trie {
    Trie[] trie = new Trie[2];
    int cnt;
}
class Solution {
    Trie root;  
    public int countPairs(int[] nums, int low, int high) {
        return get(nums, high) - get(nums, low - 1);
    }
    int get(int[] nums, int high) {
        root = new Trie();
        int ans = 0;
        for (int i = 0; i < nums.length; i++) {
            ans += query(nums[i], high);
            add(nums[i]); 
        }
        return ans;
    }
    void add(int x) {
        Trie p = root;
        for (int i = 14; i >= 0; i--) {
            int u = (x >> i)  & 1;
            if (p.trie[u] == null) p.trie[u] = new Trie();
            p = p.trie[u]; //移动到下一个结点 
            p.cnt++; // 个数增加,p.cnt代表p结点出现的次数
        }
    }
    int query(int x, int high) {
        int sum = 0;
        Trie p = root;
        for  (int i = 14; i >= 0; i--) {
            int u = (x >> i) & 1;
            if (((high >> i) & 1) == 1) { //high当前i位为1, 那么x与以前数当前i位的异或可以位1或者0
                if (p.trie[u] != null) sum += p.trie[u].cnt;//加上与x异或后当前i位为0的数量
                if (p.trie[u ^ 1] == null) return sum; //没有结点可以继续走下去,直接返回
                p = p.trie[u ^ 1]; //继续往异或的结点走下去
            } else { //high当前i位为0, x与以前数异或的第i为必须为0
                if (p.trie[u] == null) return sum; //没有结点走下去
                p = p.trie[u]; //寻找与x的第i位相同的进制,异或结果为0
            }
        }
        sum += p.cnt; //加上走到最后的结点数
        return sum;
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/142023.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

AcWing 1221. 四平方和(二分或哈希)

一、题目描述 二、思路分析 先从时间复杂度的角度入手&#xff0c;这道题的数据范围是106&#xff0c;因此我们的时间复杂度要控制在O(n)O(n)O(n)或者O(nlogn)O(nlogn)O(nlogn)。 对于abcd中的任何一个元素&#xff0c;必定是小于n\sqrt nn​的。 我们的一个思路就是去枚举&…

如何选择用 .net Framework 或 .net core

小米问&#xff1a; 给你一个项目&#xff0c;如何选择用 netframework 或 netcore&#xff1f;如何选择服务器&#xff1f; 怎么去考虑&#xff1f; 咋回答呢 答&#xff1a; 不要考虑.net framework 除非极其特殊的情况 比如目标主机系统版本较低 服务器自然是linux好&a…

2023年考证时间一览表

2022年已经成为历史&#xff0c;在疫情背景全面开放下给大家整理了2023年全年的考试时间以及报名时间新鲜出炉&#xff0c;了解清楚&#xff0c;为2023年提前做好规划&#xff01; 1月份 2022年下半年中小学教师资格考试面试 报名时间&#xff1a;2022年12月9日-12日 考试时间…

大数据:Hive视图和索引

一、视图 1.1 简介 Hive 中的视图和 RDBMS 中视图的概念一致&#xff0c;都是一组数据的逻辑表示&#xff0c;本质上就是一条 SELECT 语句的结果集。视图是纯粹的逻辑对象&#xff0c;没有关联的存储 (Hive 3.0.0 引入的物化视图除外)&#xff0c;当查询引用视图时&#xff0…

【自学C++】Windows安装C++语言开发环境

Windows安装C语言开发环境 Windows安装C语言开发环境教程 C 的开发环境可以直接使用 C 语言 的开发环境&#xff0c; 同时&#xff0c;Windows 本身就自带 C 语言的运行环境&#xff0c;因此&#xff0c;为了开发 C 语言&#xff0c;我们只需要安装一个 C 语言的开发工具即可…

第03讲:HTTP操作之ElasticSearch文档操作

3.1.2、文档操作 实验1&#xff1a;创建文档 索引已经创建好了&#xff0c;接下来我们来创建文档&#xff0c;并添加数据。这里的文档可以类比为关系型数据库中的表数据&#xff0c;添加的数据格式为 JSON 格式 在 Postman 中&#xff0c;向 ES 服务器发 POST 请求 :http://1…

【Kafka】Java实现数据的生产和消费

【Kafka】Java实现数据的生产和消费 Kafka介绍 Kafka 是由 Linkedin 公司开发的&#xff0c;它是一个分布式的&#xff0c;支持多分区、多副本&#xff0c;基于 Zookeeper 的分布式消息流平台&#xff0c;它同时也是一款开源的基于发布订阅模式的消息引擎系统。 Kafka 有如下…

【LeetCode】1803. 统计异或值在范围内的数对有多少

1803. 统计异或值在范围内的数对有多少 题目描述 给你一个整数数组 nums &#xff08;下标 从 0 开始 计数&#xff09;以及两个整数&#xff1a;low 和 high &#xff0c;请返回 漂亮数对 的数目。 漂亮数对 是一个形如 (i, j) 的数对&#xff0c;其中 0 < i < j <…

阻塞式队列

文章目录一、阻塞队列阻塞队列的概念阻塞队列相关类和方法生产者消费者模型二、自定义实现阻塞队列自定义实现循环队列自定义实现阻塞队列生产者消费者模型体验一、阻塞队列 阻塞队列的概念 队列我们并不默认&#xff0c;一提起队列&#xff0c;我们立马就能想到 "先进先…

一条 update 语句的执行过程

1.一条 update 语句的执行流程 一条更新语句&#xff0c;其实是增&#xff0c;删&#xff0c;查的综合体&#xff0c;查询语句需要经过的流程&#xff0c;更新语句全部需要执行一次&#xff0c;因为更新之前必须要先拿到&#xff08;查询&#xff09;需要更新的数据。 Buffer…

低代码平台组件间通信方案复盘

背景介绍3年前我开发了一款零代码搭建平台 H5-Dooring, 主要目的是想用更低的成本, 更快的效率, 上线 web 页面(其实是不想写重复的代码了,写麻了). 好在陆陆续续折腾了3年, 目前已经可以满足基本的页面设计和搭建能力, 并能快速上线页面.之前也在社区分享了很多低代码和零代码…

高、低成本MEMS惯导系统姿态、位置、速度更新算法的对比

高、低成本MEMS惯导系统姿态、位置、速度更新算法的对比一、高成本MEMS惯导系统姿态、位置、速度更新算法1、速度更新2、位置更新3、姿态更新4、程序仿真及实验结果4.1 主函数4.2 子函数4.3 实验结果一、低成本MEMS惯导系统姿态、位置、速度更新算法1、速度更新2、位置更新3、姿…

乾元浩在创业板IPO终止:主要生产禽用疫苗产品,中农大是股东

2023年1月4日&#xff0c;深圳证券交易所披露的信息显示&#xff0c;乾元浩生物股份有限公司&#xff08;下称“乾元浩”&#xff09;提交了撤回上市申请文件的申请&#xff0c;保荐人中信证券也撤回对该公司的保荐。因此&#xff0c;深交所终止了乾元浩首次公开发行股票并在创…

程序员述职报告

程序员述职报告笔者能力有限&#xff0c;仅供参考。做研发的小伙伴&#xff0c;不太擅长于写PPT&#xff0c;对于更高一级别的领导&#xff0c;可能只有年终述职的时候才能全面的了解你的工作。所以需要我们在PPT中表达自我价值&#xff0c;突出角色职责。让领导更清楚的认识你…

访问数据库超时问题排障

1 排障过程 系统从圣诞节那天晚上开始&#xff0c;每天晚上固定十点多到十一点多这个时段&#xff0c;大概瘫痪1h左右&#xff0c;过这时段系统自动恢复。系统瘫痪时的现象就是&#xff0c;网页和App都打不开&#xff0c;请求超时。系统架构&#xff1a; 整个系统托管在公有云…

图解数据结构:盘点链表与栈和队列的那些血缘(单双链表模拟实现栈和队列)

写在前面 Hello&#xff0c;各位盆友们&#xff0c;我是黄小黄。关于前一段时间为什么拖更这件事&#xff0c;这里给大家说一句抱歉。笔者前段时间忙于ddl和一些比赛相关的事件&#xff0c;当然还有些隐藏任务&#xff0c;所以博文更新就放缓了。  这里还需要做一下对以后博文…

计算机原理一_计算机的组成、进程与线程

目录儿一、计算机组成二、进程与线程2.1 线程的切换2.2 CPU的并发控制2.2.1 关中断2.2.2 缓存一致性协议2.2.2.1 缓存Cache2.2.2.2 缓存行Cache Line2.2.2.3 缓存一致性拓展&#xff1a;超线程2.2.3 内存屏障2.2.3.1 CPU的乱序执行拓展1&#xff1a;java 的 this 溢出问题拓展2…

Linux(一):Linux基本结构

一、Linux系统划分 linux系统分为用户区、内核区 1.1分区目标 保护数据和硬件安全&#xff0c;对系统进行分区也就是进程分区&#xff0c;当处于用户态&#xff0c;只能访问用户区&#xff0c;用户无法修改内核&#xff0c;保证硬件安全&#xff0c;操作系统不易损坏&#…

【Qt】QMainWindow应用程序窗口类简单介绍

QMainWindow介绍 QMainWindow是一个为用户提供主窗口程序的类&#xff0c;是许多应用程序的基础&#xff0c;包含的组件有&#xff1a; 菜单栏QMenuBar&#xff0c;一个主窗口最多只能有一个菜单栏&#xff1b;包含一个下拉菜单项的列表&#xff0c;这些菜单项由QAction动作类…

【git版本控制】| git版本控制操作命令(全)

文章目录一、简介二、工作模式1 集中式&#xff08;CVS、SVN&#xff09;2 分布式Git三、Git1 工作模式2 git工作流程3 工作区和版本库4 注意事项5 基本操作5.1 创建本地版本库5.2 初始化本地版本库5.3 .git目录的作用5.4 创建用户5.5 其他操作6 git分支7 常见警告8 免密登录9 …