【数据结构-并查集】力扣1202. 交换字符串中的元素

news/2025/2/21 20:51:18

给你一个字符串 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 中只含有小写英文字母

并查集

class UnionFind{
private:
    vector<int> parent;
public:
    UnionFind(int n){
        parent.resize(n);
        for(int i = 0; i < n; i++){
            parent[i] = i;
        }
    }

    void unite(int index1, int index2){
        parent[find(index2)] = find(index1);
    }

    int find(int index){
        if(parent[index] == index){
            return index;
        }
        parent[index] = find(parent[index]);
        return parent[index];
    }
};


class Solution {
public:
    string smallestStringWithSwaps(string s, vector<vector<int>>& pairs) {
        int n = s.size();
        UnionFind uf(n);

        for(auto& c : pairs){
            uf.unite(c[0], c[1]);
        }

        unordered_map<int, priority_queue<char, vector<char>, greater<>>> hash;
        for(int i = 0; i < n; i++){
            auto& c = s[i];
            hash[uf.find(i)].push(c);
        }

        string res = "";
        for(int i = 0; i < n; i++){
            res += hash[uf.find(i)].top();
            hash[uf.find(i)].pop();
        }
        return res;
    }
};

这道题首先需要掌握并查集的知识,不熟悉该数据结构的可以先去看我主页力扣990题解。这道题目我们通过观察可以发现,只要索引对之间有交互,那么这些索引对所覆盖的索引的字符之间可以通过多次交换而替换成其中的任意一个字符。通过这个特性,我们就可以将索引对之间有交集的字符作为一个连通块,连通块为了排序字符之间的字典序,所以采用了小根堆的数据结构,保证连通块之间的字符是按字典序升序排列。

那么我们最后就可以定义一个变量res来记录答案,我们只需要遍历字符串从0到n-1的索引,然后将该字符索引所在连通集中的字典序最小的字符推入到res中,然后再将最小字符从连通集的小根堆中弹出即可。

举个例子
输入:s = “dcab”, pairs = [[0,3],[1,2]]
字符d和b是一个连通集,字符c和a是一个连通集。
那么在计算res的时候,遍历字符串s,i=0所在连通集b是字典序最小字符,那么就将b推入res中,然后i=1所在连通集a是字典序最小的字符,将a推入到res中,再遍历到i=2,此时该连通集的小根堆中只剩下了c,那么只能推入c到res中,最后i=3,此时该连通集中的小根堆只剩下了d,推入d到res中,最后输出:“bacd”。


http://www.niftyadmin.cn/n/5861308.html

相关文章

html - - - - - modal弹窗出现时,页面怎么能限制滚动

html - - - - - 弹出出现时&#xff0c;页面怎么能限制滚动 1. 全局添加css样式2. 更改弹窗状态时的操作 1. 全局添加css样式 .no-scroll {overflow: hidden;height: 100vh; /* 防止移动端地址栏隐藏导致的页面跳动 */ }2. 更改弹窗状态时的操作 if(show){// 打开弹窗&#…

OpenCV中的边缘检测

边缘检测是图像处理和计算机视觉中的关键技术之一&#xff0c;旨在识别图像中像素强度发生显著变化的区域&#xff0c;这些区域通常对应于物体的边界或轮廓。边缘检测在机器视觉中具有重要的需求背景&#xff0c;主要体现在以下几个方面&#xff1a; 图像分割&#xff1a;边缘…

Linux 下 VIM 编辑器学习记录:从基础到进阶(中)

在 Linux 系统的学习与实践过程中&#xff0c;对文件内容的查看是一项极为基础且高频的操作。熟练掌握各类内容查看命令&#xff0c;不仅能提升我们在 Linux 环境下的工作效率&#xff0c;对于学习 Java 全栈开发的同学来说&#xff0c;在处理项目相关的配置文件、日志文件时也…

C#的序列化[Serializable()]

[Serializable] 是 .NET 框架中的一个特性&#xff08;Attribute&#xff09;&#xff0c;用于标记一个类、结构体、枚举或委托可以被序列化。序列化是将对象的状态转换为可以存储或传输的格式&#xff08;如二进制、XML 或 JSON&#xff09;的过程&#xff0c;以便在需要时可以…

C语言学习,插入排序

C语言&#xff0c;插入排序是一种简单直观的排序算法&#xff0c;插入排序是通过构建有序序列&#xff0c;对于未排序数据&#xff0c;在已排序序列中从后向前扫描&#xff0c;找到相应位置并插入。 示例&#xff1a; // 插入排序函数 void insertionSort(int arr[], int n)…

ref() 和 reactive()响应性 浅解

文章目录 1. ref() 和 reactive() 的区别2. 解构 详解2.1. 什么是解构2.2. 解构避免丢失响应性的办法2.2.1. 解决方案&#xff1a;toRefs() 保持响应性2.2.2. 解决方案&#xff1a; toRef()保持响应性 3. 最佳实践 在 Vue 3 中&#xff0c;ref() 和 reactive() 都是用于响应式数…

2024华为OD机试真题-恢复数字序列(C++/Java/Python)-E卷-100分

2024华为OD机试最新E卷题库-(C卷+D卷+E卷)-(JAVA、Python、C++) 目录 题目描述 输入描述 输出描述 用例1 题目解析 代码 c++ Java python 题目描述 对于一个连续正整数组成的序列,可以将其拼接成一个字符串,再将字符串里的部分字符打乱顺序。 如序列8 9 10 11 12…

javaSE学习笔记22-线程(thread)-线程通信、线程池

线程通信 应用场景&#xff1a;生产者和消费者问题 假设仓库中只能存放一件产品&#xff0c;生产者将生产出来的产品放入仓库&#xff0c;消费者将仓库中产品取走消费 如果仓库中没有产品&#xff0c;则生产者将产品放入仓库&#xff0c;否则停止生产并等待&#xff0c…