字典序最小的01字符串
问题描述
小U拥有一个由0和1组成的字符串,她可以进行最多k次操作,每次操作可以交换相邻的两个字符。目标是通过这些操作,使得最终得到的字符串字典序最小。
例如,小U当前有一个字符串 01010,她最多可以进行 2 次相邻字符交换操作。通过这些操作,她可以将字符串调整为 00101,这是可以通过不超过2次操作得到的字典序最小的字符串。
现在,小U想知道,经过最多k次操作后,能够得到的字典序最小的字符串是什么。
测试样例
样例1
输入:n = 5, k = 2, s = "01010"
输出:'00101'
样例2
输入:n = 7, k = 3, s = "1101001"
输出:'0110101'
样例3
输入:n = 4, k = 1, s = "1001"
输出:'0101'
算法设计
- 分析题目过后首先要明确最重要的一点,让尽可能多的0到字符串的前段,才能达到最小的状态
- 初始化:将输入的字符串转换为字符数组,方便后续的操作。
- 执行操作:循环 k 次,每次从左至右遍历字符串,寻找第一个满足条件的相邻字符对(即左边是 1 右边是 0),并将它们交换。这样可以确保尽可能多的 0 移动到前面,从而减少字典序。
- 构建结果:完成所有操作后,重新组合字符数组形成字符串并返回。
复杂度
初始化
- 时间复杂度:将字符串转换为 runes 切片的时间复杂度为 O(n)
交换操作
- 外层循环:进行 k 次交换操作,时间复杂度为 O(k)
- 内层循环:每次交换操作需要从左到右遍历字符串,时间复杂度为 O(n)
因此,交换操作的总时间复杂度为 O(k⋅n)。
总体时间复杂度
- 初始化:O(n)
- 交换操作:O(k⋅n) 总体时间复杂度为 O(k⋅n)。
空间复杂度分析
- runes 切片:存储字符串的 runes,空间复杂度为 O(n)。
总体空间复杂度为 O(n)。
代码
package main
import (
"fmt"
"strings"
)
func solution(n int, k int, s string) string {
ch := []rune(s)
for i := 0; i < k; i++ {
for j := 0; j < n-1; j++ {
if ch[j] == '1' && ch[j+1] == '0' {
temp := ch[j]
ch[j] = ch[j+1]
ch[j+1] = temp
break
}
}
}
ss := strings.Builder{}
for _, c := range ch {
ss.WriteRune(c)
}
return ss.String()
}
测试
func main() {
fmt.Println(solution(5, 2, "01010") == "00101")
fmt.Println(solution(7, 3, "1101001") == "0110101")
fmt.Println(solution(4, 1, "1001") == "0101")
}
优化
当前的算法在每次交换操作中都从头开始遍历字符串,这导致了较高的时间复杂度。可以通过以下方式进行优化:
- 记录 1 的位置:使用一个队列或双端队列来记录所有 1 的位置,每次交换时直接从队列中取出下一个 1 的位置进行交换。
- 减少不必要的遍历:通过记录 1 的位置,可以避免每次都从头开始遍历字符串。
优化后的代码
package main
import (
"fmt"
"strings"
)
// solution 解决字典序最小的01字符串问题
func solution(n int, k int, s string) string {
runes := []rune(s)
ones := make([]int, 0)
// 记录所有 '1' 的位置
for i := 0; i < n; i++ {
if runes[i] == '1' {
ones = append(ones, i)
}
}
for i := 0; i < k && len(ones) > 0; i++ {
pos := ones[0]
if pos > 0 && runes[pos-1] == '0' {
runes[pos-1], runes[pos] = runes[pos], runes[pos-1]
ones = append(ones[:0], ones[1:]...) // 移除第一个 '1' 的位置
ones = append(ones, pos-1) // 添加新的 '1' 的位置
} else {
ones = append(ones[:0], ones[1:]...) // 移除第一个 '1' 的位置
}
}
return string(runes)
}
func main() {
fmt.Println(solution(5, 2, "01010")) // 00101
fmt.Println(solution(7, 3, "1101001")) // 0110101
fmt.Println(solution(4, 1, "1001")) // 0101
}
优化后的时间复杂度
- 初始化:记录所有 1 的位置的时间复杂度为 O(n)。
- 交换操作:每次交换操作的时间复杂度为 O(1),总共进行 k 次交换操作,时间复杂度为 O(k)。
总体时间复杂度为 O(n+k)。
优化后的空间复杂度
- runes 切片:存储字符串的 runes,空间复杂度为 O(n)。
- ones 切片:存储所有 1 的位置,空间复杂度为 O(n)。
总体空间复杂度为 O(n)。