并查集的妙用——Leetcode 1202

2023-10-31

并查集的妙用——Leetcode 1202

    给你一个字符串 s,以及该字符串中的一些「索引对」数组 pairs,其中 
pairs[i] = [a, b] 表示字符串中的两个索引(编号从 0 开始)。
    你可以 任意多次交换 在 pairs 中任意一对索引处的字符。
    返回在经过若干次交换后,s 可以变成的按字典序最小的字符串。

 

示例 1:
输入:s = "dcab", pairs = [[0,3],[1,2]]
输出:"bacd"
解释: 
交换 s[0] 和 s[3], s = "bcad"
交换 s[1] 和 s[2], s = "bacd"

示例 2:
输入:s = "dcab", pairs = [[0,3],[1,2],[0,2]]
输出:"abcd"
解释:
交换 s[0] 和 s[3], s = "bcad"
交换 s[0] 和 s[2], s = "acbd"
交换 s[1] 和 s[2], s = "abcd"

示例 3:
输入:s = "cba", pairs = [[0,1],[1,2]]
输出:"abc"
解释:
交换 s[0] 和 s[1], s = "bca"
交换 s[1] 和 s[2], s = "bac"
交换 s[0] 和 s[1], s = "abc"

提示:
1 <= s.length <= 10^5
0 <= pairs.length <= 10^5
0 <= pairs[i][0], pairs[i][1] < s.length
s 中只含有小写英文字母

这个题的思路是很容易想到的,pairs把下标分成若干组,其中每一组内的下标,元素是可以以任意顺序排列的。那么只需要把这些下标分成若干组,在把每组内的下标对应的元素升序排列,就可以得到答案了。很显然,想要找到互相有联系的下标,就要用到并查集。 第一次打出来的代码如下:

class Solution 
{
public:
    string smallestStringWithSwaps(string s, vector<vector<int>>& pairs) 
    {   
        unordered_map<int, int> hash;
        for (int i = 0; i < pairs.size(); ++i)
        {
            if (findUnion(pairs[i][0], hash) != 
            	findUnion(pairs[i][1], hash))
            {
                hash[findUnion(hash[pairs[i][1]], hash)] = 
                    findUnion(pairs[i][0], hash);
            }
        }

        vector<bool> flags(s.size(), 1);
        int cur_union = -1;
        for (bool b = 0; ; b = 0, cur_union = -1)
        {
            string str;
            vector<int> idx;
            for (int i = 0; i < s.size(); ++i)
            {
                if (flags[i])    //当前字符s[i]可以交换
                {
                    if (cur_union == -1)    //当前还未有联盟
                    {
                        flags[i] = 0;    //s[i]参与交换
                        cur_union = findUnion(i, hash);    //确定联盟
                        idx.push_back(i);    //i放入idx
                        str += s[i];    //s[i]放入str
                        b = 1;    //标记有参与交换
                    }
                    else    //当前已有联盟
                    {
                        if (cur_union == findUnion(i, hash))    
                        	//s[i]属于当前联盟
                        {
                            flags[i] = 0;
                            idx.push_back(i);
                            str += s[i];
                        }
                    }
                }
            }

            if (!b) { break; }
            
            sort(str.begin(), str.end());
            for (int i = 0; i < str.size(); ++i)
            {
                s[idx[i]] = str[i];
            }
        }
        return s;
    }

    int findUnion(int i, unordered_map<int, int>& hash)
    {
        if (hash.find(i) == hash.end())    
        	//map和unordered_map的key在未插入的情况下,访问这个key,会自
        	//动把key的value赋值为0
        {
            return hash[i] = i;
        }
        if (hash[i] != i)
        {
            hash[i] = findUnion(hash[i], hash);
        }
        return hash[i];
    }
};

这份代码并不是很优秀的,主要有以下几个问题:
· map和unordered_map的key在未插入的情况下,访问这个key,会自动把key的value赋值为0,因此findUnion函数要先判断是否存在key;
· 所有下标分组后,对每个组的下标对应的元素进行排序的过程过于繁琐

因此,代码可以做以下改进。首先,用vector代替unordered_map,作为并查集;其次可以对分好组的下标对应的元素,用代码所示的方法进行排序:

class Solution 
{
public:
    string smallestStringWithSwaps(string s, vector<vector<int>>& pairs) 
    {   
        int n = s.size();
        for (int i = 0; i < n; ++i)
        {
            parent.push_back(i);
        }

        for (auto& it : pairs)
        {
            int px = findUnion(it[0]), py = findUnion(it[1]);
            if (px != py)
            {
                parent[px] = py;
            }
        }

        unordered_map<int, vector<int> > mem;
        	//key是“盟主”, value是个向量,保存“联盟”的所有下标对应的字符
        for (int i = 0; i < n; ++i)
        {
            mem[findUnion(i)].push_back(s[i]);
        }

        for (auto& it : mem)
        {
            sort(it.second.begin(), it.second.end(), greater<int>()); 
            	//降序排序,方便到时候先取出最小的
        }

        string res;
        for (int i = 0; i < n; ++i) 
        {
            int x = findUnion(i);    //找到“盟主”
            res.push_back(mem[x].back());    //拿出“联盟”中最小的字符
            mem[x].pop_back();    //删除这个字符
        }
        return res;
    }

    int findUnion(int i)
    {
        if (parent[i] != i)
        {
            parent[i] = findUnion(parent[i]);
        }
        return parent[i];
    }

    vector<int> parent;
};

更多精彩内容,敬请期待。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

并查集的妙用——Leetcode 1202 的相关文章

  • 搞事情之使用七牛云的注意事项

    原文地址 PJ 的 iOS 开发日常 前言 本博客最初所采用的图床就是七牛 当时因为第一次使用图床之类的服务 没有进行一个比较好的筛选 并且没有考虑过多的细节 所以直接采用了七牛 经过一段时间后 因为博客访问量上去了 超出七牛每月的免费流量

随机推荐

  • C++ malloc/free/new/delete详解(内存管理)

    这里写目录标题 malloc free 典型用法 内存分配 实现过程 brk和mmap 申请小于128k的内存 申请大于128k的内存 释放内存 brk和mmap的区别 new delete 典型用法 内存分配 实现过程 new delet
  • 生信入门(六)——单细胞分析(Seurat)

    生信入门 六 单细胞分析 Seurat 文章目录 生信入门 六 单细胞分析 Seurat 一 数据导入 1 数据来源 2 数据导入 二 标准预处理 1 QC和选择细胞进行进一步分析 2 规范化数据 3 识别高度可变的特征 特征选择 4 缩放
  • Win10家庭版安装Docker for windows遇坑总结

    Win10家庭版安装Docker for windows遇坑总结 安装前的简单了解 安装步骤 添加Hyper v 安装Docker for windows 其他问题 因为做毕设需要结合本组学长开发的系统 不得已开始入坑学习docker 遇到
  • 程序员面试题精选100题(44)-数值的整数次方

    程序员面试题精选100题 44 数值的整数次方 题目 实现函数double Power double base int exponent 求base的exponent次方 不需要考虑溢出 方法一 由于输入的exponent是个int型的数值
  • AR-虚实融合文献阅读整理(二)

    一 增强现实中虚实融合和人机交互技术的研究与应用 黄震宇 基于标志物的识别 利用opencv和三维图形引擎OGRE实现虚实融合展示系统 人机交互方案采用PrimeSense的深度摄像头 通过计算机视觉处理 重建了人体三维谷歌系统定义体感语义
  • C++:CMake常用变量【CMAKE_CXX_FLAGS、CMAKE_BUILD_TYPE、×_BINARY_DIR】

    CMake共用七种变量 如下所示 提供信息的变量 控制变量 描述系统的变量 控制构建过程的变量 语言变量 CTest变量 CPack变量 一 CMake变量引用的方式 使 进 变量的引 在 IF 等语句中 是直接使 变量名 不通过 取值 二
  • Linux系统中/etc/rc.local和/etc/rc.d/rc.local的区别

    etc rc d rc local 用于添加开机启动命令 etc rc local是 etc rc d rc local的软连接 转载于 https www cnblogs com Samuel Leung p 10477162 html
  • 【Spring

    上文讲了 Spring 资源处理 本文讲一下resource的扩展接口相关 资源处理扩展 ResourceLoader 接口 定义 图解 示例 策略 ResourcePatternResolver接口 ResourceLoaderAware
  • 实例修改类属性python_Python类属性和实例属性的优先级

    可以看到 属性可以分为类属性和实例属性 那么问题就来了 如果类属性和实例属性名字相同时 会怎么样 这就涉及Python中类属性和实例属性的优先级的问题了 我们可以做一个实验 在前面类定义的基础上 在实例属性中 也初始化一个localtion
  • DS18B20温度传感器原理及使用教程

    1 芯片简介 DS18B20数字温度传感器提供9 Bit到12 Bit的摄氏温度测量精度和一个用户可编程的非易失性且具有过温和低温触发报警的报警功能 DS18B20采用的1 Wire通信即仅采用一个数据线 以及地 与微控制器进行通信 该传感
  • Linux下安装/使用mariadb

    文章目录 第一章 mariadb在rhel7上的使用 第二章 mariadb在rhel6上的安装 1 编译源码包 比较慢 2 二进制包安装 比较推荐 第一章 mariadb在rhel7上的使用 rhel7及以上系统默认安装了mariadb
  • C#基础入门之数据类型

    一 值类型和引用类型 在C 中数据类型总共可以分为两类 分别是值类型和引用类型 值类型 表示复制一个当前变量传给方法 当你在这个方法中改变这个变量的值时 最初生命的变量的值不会变 引用类型 表示你操作的数据是同一个 也就是说当你传一个参数给
  • 物联网面试必过要点

    要是能熟记以下知识点 再加上自身的项目经验 过个面试 问题不大 指针定义 一个指向指针的的指针 它指向的指针是指向一个整型数 int a 一个有10个指针的数组 该指针是指向一个整型数的 int a 10 一个指向有10个整型数数组的指针
  • bind的原理和bind的实现

    一 bind的特性 传递的第一个参数做为调用它的函数的this指向 bind可传递若干参数 若第一个参数传递基础数据类型 则调用他的函数的this指向该基础数据类型的包装类实例化对象 若第一个参数为null或undefined 则调用他的函
  • 数据库操作 - 关系模型

    关系数据库是建立在关系模型上的 而关系模型本质上就是若干个存储数据的二维表 可以把它们看作很多Excel表 gt 表的每一行称为记录 Record 记录是一个逻辑意义上的数据 gt 表的每一列称为字段 Column 同一个表的每一行记录都拥
  • 并查集、树状数组

    并查集 树状数组 线段树 并查集 树状数组 树状数组1 单点修改 区间查询 树状数组2 单点查询 区间修改 并查集 模板 并查集 题目描述 如题 现在有一个并查集 你需要完成合并和查询操作 输入格式 第一行包含两个整数 N M N M N
  • 清华镜像用法

    用pip安装模块时 总是会报错 大片红字 请求超时 影响心情 如果使用镜像安装 就会很顺 敲一下回车键 一两秒就搞定 节约时间 平常简单用法是 pip install beautifulsoup4 加入镜像参数后 pip install b
  • 任意进制转换(c++)

    include
  • OpenHarmony源码解析(12): hisysevent

    1 概述 HiSysEvent是面向OpenHarmony系统开发者提供的系统打点功能 通过在关键路径埋点来记录系统在运行过程中的重要信息 辅助开发者定位问题 此外还支持开发者将打点数据上传到云进行大数据质量度量 HiSysEvent包括H
  • 并查集的妙用——Leetcode 1202

    并查集的妙用 Leetcode 1202 给你一个字符串 s 以及该字符串中的一些 索引对 数组 pairs 其中 pairs i a b 表示字符串中的两个索引 编号从 0 开始 你可以 任意多次交换 在 pairs 中任意一对索引处的字