一、题目描述
给你一个字符串 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 中只含有小写英文字母
二、分析
并查集经典题目。
解决方案:并查集+哈希表+排序
三、实现
type UnionFind struct {
parent []int
}
func NewUnionFind(size int) *UnionFind {
uf := &UnionFind{
parent: make([]int, size),
}
for i := range uf.parent {
uf.parent[i] = i
}
return uf
}
func (uf *UnionFind) Find(x int) int {
if uf.parent[x] != x {
uf.parent[x] = uf.Find(uf.parent[x])
}
return uf.parent[x]
}
func (uf *UnionFind) Union(x, y int) {
rootx := uf.Find(x)
rooty := uf.Find(y)
if rootx != rooty {
if rootx < rooty {
uf.parent[rooty] = rootx
} else {
uf.parent[rootx] = rooty
}
}
}
func smallestStringWithSwaps(s string, pairs [][]int) string {
// 1. 使用并查集,合并可以交换的索引列
uf := NewUnionFind(len(s))
for _, pair := range pairs {
uf.Union(pair[0], pair[1])
}
// 2. 将相同索引列的项,按照并查集结果,合并组
groups := make([][]byte, len(s))
for i := range s {
group := uf.Find(i)
groups[group] = append(groups[group], s[i])
}
// 3. 各组进行升序排列(优化,可以使用优先队列)
for _, group := range groups {
sort.Slice(group, func(i, j int) bool {
return group[i] < group[j]
})
}
// 4. 依据并查集结果,返回数据
ans := make([]byte, len(s))
for i := range s {
group := uf.Find(i)
ans[i] = groups[group][0]
groups[group] = groups[group][1:]
}
return string(ans)
}
评论