LeetCode-1202. Exchange elements in a string

description

Give you a string s, and some "index pair" array pairs in the string, where pairs[i] = [a, b] represents two indexes in the string (numbering starts from 0).

You can swap the characters at any pair of indices in pairs as many times as you want.

Return the smallest string lexicographically that s can become after several exchanges.

 

Example 1:

Input: s = "dcab", pairs = [[0,3],[1,2]]
Output: "bacd"
Explanation: 
exchange s[0] and s[3], s = "bcad"
exchange s[1 ] And s[2], s = "bacd"
Example 2:

Input: s = "dcab", pairs = [[0,3],[1,2],[0,2]]
Output: "abcd"
Explanation:
Exchange s[0] and s[3], s = " "bcad"
exchanges s[0] and s[2], s = "acbd"
exchanges s[1] and s[2], s = "abcd"
Example 3:

Input: s = "cba", pairs = [[0,1],[1,2]]
Output: "abc"
Explanation:
exchange s[0] and s[1], s = "bca"
exchange s[1 ] And s[2], s = "bac"
swap s[0] and s[1], s = "abc"
 

prompt:

1 <= s.length <= 10^5
0 <= pairs.length <= 10^5
0 <= pairs[i][0], pairs[i][1] <s.length
s contains only lowercase English letter

Source: LeetCode
Link: https://leetcode-cn.com/problems/smallest-string-with-swaps/

Solve

    // 辅助实现类,并查集
    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;
        }
    };

 

Guess you like

Origin blog.csdn.net/u010323563/article/details/112474131