説明
文字列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;
}
};