LeetCode-1202。文字列内の要素を交換します

説明

文字列sと、文字列内のいくつかの「インデックスペア」配列ペアを指定します。ここで、pairs [i] = [a、b]は、文字列内の2つのインデックスを表します(番号は0から始まります)。

インデックスの任意のペアの文字を、ペアで何度でも交換できます。

sが数回の交換後になり得る最小の文字列を辞書式に返します。

 

例1:

入力:s = "dcab"、ペア= [[0,3]、[1,2]]
出力: "bacd"
説明: 
交換s [0]およびs [3]、s = "bcad"
交換s [1 ]およびs [2]、s = "bacd"
例2:

入力:s = "dcab"、ペア= [[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"、ペア= [[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 <= pair.length <= 10 ^ 5
0 <= pair [i] [0]、pairs [i] [1] <s.lengths
には小文字の英語文字のみが含まれます

ソース: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
おすすめ