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 中只含有小写英文字母

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/smallest-string-with-swaps/

求解

    // 辅助实现类,并查集
    class UnionFind {
    public:
        UnionFind(int n) {
            rank.reserve(n);
            parent.reserve(n);
            for (int i = 0; i < n; ++i) {
                rank[i] = 1;
                parent[i] = i;
            }
            count = n;
        }

        bool isConnected(int p, int q) {
            assert(p < count);
            assert(q < count);
            return find(p) == find(q);
        }

        void unionElements(int p, int q) {
            assert(p < count);
            assert(q < count);
            int pParent = find(p);
            int qParent = find(q);
            if (pParent == qParent) {
                return;
            }
            if (rank[pParent] < rank[qParent]) {
                parent[pParent] = qParent;
                return;
            }
            if (rank[pParent] > rank[qParent]) {
                parent[qParent] = pParent;
                return;
            }
            // rank[pParent] == rank[qParent]
            parent[pParent] = qParent;
            ++rank[qParent];
        }

        int find(int p) {
            assert(p < count);
            while (p != parent[p]) {
                parent[p] = parent[parent[p]];
                p = parent[p];
            }
            return p;
        }

    private:
        vector<int> parent;
        int count;
        vector<int> rank;
    };

    class Solution {
    private:
        vector<bool> visited; // 标识点是否被访问
        vector<int> id; // 每个点的连通分量
        int count = 0; // 连通分量
        void dfs(const vector<vector<int>> &edges, int v) {
            visited[v] = true;
            id[v] = count;
            for (auto w : edges[v]) {
                if (!visited[w]) {
                    dfs(edges, w);
                }
            }
        }

    public:
        // 方法一,用并查集对每个连通分量内部字符进行排序
        string smallestStringWithSwaps_useUnionFind(string s, vector<vector<int>> &pairs) {
            const int n = s.size();
            if (n < 2) {
                return s;
            }

            // 利用所有可以交换的点信息构造并查集
            UnionFind uf(n);
            for (const auto &pair : pairs) {
                uf.unionElements(pair[0], pair[1]);
            }
            // 计算并查集的连通分量
            std::unordered_map<int, vector<int>> record; // 记录每一个连通分量内的所有位置字符
            for (int i = 0; i < n; ++i) {
                int ip = uf.find(i);
//                if (record.count(ip) == 0) {
//                    record.emplace(ip, vector<int>{i});
//                    continue;
//                }
//                auto &vec = record[ip];
//                vec.push_back(i);

                // 啰嗦的代码优化
                // 这儿不用去判断ip在散列表中是否存在了,value是一个vector,总之都是要构造一个vector的
                record[ip].push_back(i);
            }

            // 在每一个联通分量内部对字符串进行排序
            string res = s;
            for (auto &[k, vec] : record) {
                // 循环内部实现方法一
                // 使用carr辅助空间存放每个连通分量的所有字符
                /*vector<char> carr;
                carr.reserve(vec.size());
                for (int index : vec) {
                    carr.emplace_back(res[index]);
                }
                // 每个连通分量的位置、字符排序
                std::sort(vec.begin(), vec.end()); // 每个连通分量内部的位置按照从小到大排序
                std::sort(carr.begin(), carr.end()); // 每个连通分量内部字符按照字典序进行排序
                for (int i = 0; i < vec.size(); ++i) {
                    // 将排序好的字符从新赋值回原来的连通分量位置(位置从小到大,字符按照字典序从小到大)
                    res[vec[i]] = carr[i];
                }*/

                // 循环内部实现方法二
                // 使用一个数组辅助空间存放每个连通分量的原来位置,直接对位置进行排序
                vector<int> cp = vec; // 位置留存一份副本
                std::sort(vec.begin(), vec.end(), [&](int a, int b) { return s[a] < s[b]; });
                for (int i = 0; i < vec.size(); ++i) {
                    res[cp[i]] = s[vec[i]]; // 理解
                }
            }
            return res;
        }

        // 方法二,利用图的深度遍历得到连通分量
        string smallestStringWithSwaps(string s, vector<vector<int>> &pairs) {
            const int n = s.size();
            if (n < 2) {
                return s;
            }
            // 构造图
            vector<vector<int>> edges; // 利用邻接表存储图
            edges.resize(n, vector<int>());
            for (auto &pair : pairs) {
                edges[pair[0]].push_back(pair[1]);
                edges[pair[1]].push_back(pair[0]);
            }


            // 初始化状态,深度遍历图
            visited.assign(n, false);
            id.assign(n, 0);
            count = 0;
            for (int i = 0; i < n; ++i) {
                if (!visited[i]) {
                    ++count;
                    dfs(edges, i);
                }
            }

            // 图深度遍历完成,得到不同的连通分量,处理同方法一中后半的部分
            std::unordered_map<int, vector<int>> record; // 记录每一个连通分量内的所有位置字符
            for (int i = 0; i < n; ++i) {
                // 仅处理连通分量大于0的位置,等于0的位置表征不进行任何交换
                if (id[i] != 0) {
                    record[id[i]].push_back(i);
                }
            }

            // 在每一个联通分量内部对字符串进行排序
            string res = s;
            for (auto &[k, vec] : record) {
                // 使用一个数组辅助空间存放每个连通分量的原来位置,直接对位置进行排序
                vector<int> cp = vec; // 位置留存一份副本
                std::sort(vec.begin(), vec.end(), [&](int a, int b) { return s[a] < s[b]; });
                for (int i = 0; i < vec.size(); ++i) {
                    res[cp[i]] = s[vec[i]]; // 理解
                }
            }
            return res;
        }
    };

猜你喜欢

转载自blog.csdn.net/u010323563/article/details/112474131