使用滑动窗口解决,需要解决的问题有
- 如何初始化left 与 right?
- 如何控制left与right的递增?
按照这个思路搓出来第一版错误代码。
基本的思路:先针对输入参数n为 1 或 2 的情况直接返回固定字符串结果,接着遍历输入数组array_a来标记出其中的优质数组元素(满足大于相邻前后元素的元素),然后使用滑动窗口机制,从左向右移动窗口,在右移窗口时只要窗口内元素总和不超过k就持续扩展右区间,之后统计当前窗口内的优质章节数量,若该数量大于已记录的最大优质章节数量就更新相关记录(最大优质章节数量以及对应的左右区间标记),最后通过缩减左区间继续探索其他可能情况,最终将最大优质章节数量、对应的左右区间标记拼接成字符串作为结果返回并打印。
这个代码的问题在于题目要的是最短的区间,而下面代码无法满足
package main
import (
"fmt"
"strconv"
)
func solution(n int, k int, array_a []int) string {
if n == 1 {
return "0,1,1"
}
if n == 2 {
return "0,1,2"
}
// 遍历一遍数组判断并记录所有的优质数组
goodChapter := make([]int, n)
for i := 1; i < n - 1; i++ {
if array_a[i] > array_a[i - 1] && array_a[i] > array_a[i + 1] {
goodChapter[i] = 1
}
}
// 使用滑动窗口尝试找到最多优质章节的区间, 需要注意此处left right为左闭右开区间
left, right := 0, 0
wordSum := 0
maxGoodChapter := 0
leftMark := 1
rightMark := 1
// 控制右区间指针
for right < n {
// 右区间扩展 不能用循环,因为要字数最少的
if right < n && (wordSum + array_a[right]) <= k {
wordSum += array_a[right]
right++
}
// 统计left与right之间的优质章节数量
count := 0
for i := left + 1; i < right - 1; i++ {
if goodChapter[i] == 1 {
count++
}
}
if count > maxGoodChapter {
maxGoodChapter = count
leftMark = left + 1
rightMark = right
}
// 处理左区间,左区间往右缩减
wordSum -= array_a[left]
left++
}
result := strconv.Itoa(maxGoodChapter) + "," + strconv.Itoa(leftMark) + "," + strconv.Itoa(rightMark)
fmt.Println(result)
return result
}
func main() {
// You can add more test cases here
fmt.Println(solution(8, 15000, []int{1000, 3000, 2000, 4000, 3000, 2000, 4000, 2000}) == "2,1,5")
fmt.Println(solution(8, 15000, []int{2000, 5000, 2000, 1000, 4000, 2000, 4000, 3000}) == "2,4,8")
}
为了解决这个问题想了一个很复杂的right与left会回退的版本。
基本的思路是:既然题目要求要获取最优短的区间,那我在每次判断优质章节区间之前,都对left与right做处理,将left右移知道再动会导致优质章节数减少,同时左移right直至再次左移会导致优质章节数减少。这样搞下来每次记录的都是可能的最短的优质章节区间。
但是因为这个思路转化为代码时,对于left与right的操作过于复杂,所以没有搓出来正确运行的代码。
package main
import (
"fmt"
"math"
)
func solution(n int, k int, array_a []int) string {
if n == 1 {
return "0,1,1"
}
if n == 2 {
return "0,1,2"
}
left, right := 0, 0
currentSum := 0
bestLeft, bestRight := 0, 0
bestQualityCount := 0
bestSum := math.MaxInt32
// 遍历一遍数组判断并记录所有的优质数组
goodChapter := make([]int, n)
for i := 1; i < n - 1; i++ {
if array_a[i] > array_a[i - 1] && array_a[i] > array_a[i + 1] {
goodChapter[i] = 1
}
}
for right < n {
// 扩展窗口
tempCount := 0
flag := 0
for right < n && currentSum <= k {
if right == n - 1 {
flag = 1
}
currentSum += array_a[right]
right++
if right - 2 >= left + 1 && goodChapter[right - 2] == 1 {
tempCount++
}
}
// 如果目前区间没有优质章节
if tempCount == 0 {
left = right - 2
continue
} else {
// right 回退到最右优质章节
for goodChapter[right - 2] != 1 {
right--
currentSum -= array_a[right]
}
// left 缩小区间到最左优质章节
for goodChapter[left + 1] != 1 {
currentSum -= array_a[left]
left++
}
// 计算当前区间长度,并更新结果
if tempCount > bestQualityCount || (tempCount == bestQualityCount && currentSum < bestSum ) {
bestQualityCount = tempCount
bestLeft = left + 1
bestRight = right
bestSum = currentSum
}
left++
}
if flag == 1 {
break
}
}
// 返回结果
fmt.Println(bestQualityCount, bestLeft+1, bestRight)
return fmt.Sprintf("%d,%d,%d", bestQualityCount, bestLeft+1, bestRight)
}
// func calculateQualityCount(array_a []int, left, right int) int {
// qualityCount := 0
// for i := left + 1; i < right - 1; i++ {
// if array_a[i] > array_a[i-1] && array_a[i] > array_a[i+1] {
// qualityCount++
// }
// }
// return qualityCount
// }
func main() {
// You can add more test cases here
fmt.Println(solution(8, 15000, []int{1000, 3000, 2000, 4000, 3000, 2000, 4000, 2000}) == "2,1,5")
fmt.Println(solution(8, 15000, []int{2000, 5000, 2000, 1000, 4000, 2000, 4000, 3000}) == "2,4,8")
}
其实不用这么复杂,先用滑动窗口找到最大优质章节最多的窗口,找到答案后再调整left与right就好,没必要在窗口遍历的过程中去做缩减。
具体而言就是在第一版代码的末尾添加一段用于缩减区间的代码。
package main
import "fmt"
func solution(n int, k int, array_a []int) string {
left, right := 0, 0
maxQualityCount := 0
start, end := 0, 0
currentSum := 0
// 遍历一遍数组判断并记录所有的优质数组
goodChapter := make([]int, n)
for i := 1; i < n - 1; i++ {
if array_a[i] > array_a[i - 1] && array_a[i] > array_a[i + 1] {
goodChapter[i] = 1
}
}
for right < n {
// 扩展窗口
currentSum += array_a[right]
right++
// 如果窗口内的总字数超过 k,收缩窗口
for currentSum > k {
currentSum -= array_a[left]
left++
}
// 计算当前窗口内的优质章节数量
qualityCount := 0
for i := left + 1; i < right - 1; i++ {
if array_a[i] > array_a[i-1] && array_a[i] > array_a[i+1] {
qualityCount++
}
}
// 更新最大优质章节数量及其对应的起始和结束位置
if qualityCount > maxQualityCount {
maxQualityCount = qualityCount
start = left
end = right - 1
}
}
// 尝试缩减区间
left = start
right = end
for goodChapter[left + 1] != 1 {
left++
}
for goodChapter[right - 1] != 1 {
right--
}
return fmt.Sprintf("%d,%d,%d", maxQualityCount, left + 1, right + 1)
}
func main() {
// You can add more test cases here
fmt.Println(solution(8, 15000, []int{1000, 3000, 2000, 4000, 3000, 2000, 4000, 2000}) == "2,1,5")
fmt.Println(solution(8, 15000, []int{2000, 5000, 2000, 1000, 4000, 2000, 4000, 3000}) == "2,4,8")
}
事实证明还是有问题,及时我们解决了初版代码无法找到最短区间的问题,但认为满足题目的要求,也就是无法找到最小字数区间。为了解决该问题,需要创造切片储存优质章节数与最大优质章节数相同的区间的left与right,在缩减后再比较字数,最终获取最优解。
def solution(n, k, array_a):
left, right = 0, 0
maxQualityCount = 0
currentSum = 0
candidates = [] # 储存所有优质章节数与最大优质章节数相同的区间的left与right
# 遍历一遍数组判断并记录所有的优质数组
goodChapter = [0] * n
for i in range(1, n - 1):
if array_a[i] > array_a[i - 1] and array_a[i] > array_a[i + 1]:
goodChapter[i] = 1
while right < n:
# 扩展窗口
currentSum += array_a[right]
right += 1
# 如果窗口内的总字数超过 k,收缩窗口
while currentSum > k:
currentSum -= array_a[left]
left += 1
# 计算当前窗口内的优质章节数量
qualityCount = 0
for i in range(left + 1, right - 1):
if array_a[i] > array_a[i-1] and array_a[i] > array_a[i+1]:
qualityCount += 1
# 更新最大优质章节数量及其对应的起始和结束位置
if qualityCount > maxQualityCount:
maxQualityCount = qualityCount
candidates = [(left, right - 1)]
elif qualityCount == maxQualityCount:
candidates.append((left, right - 1))
# 尝试缩减区间并选出字数最少的最优区间
best_left, best_right = candidates[0]
min_length = sum(array_a[best_left:best_right + 1])
for left, right in candidates:
while left + 1 < n and goodChapter[left + 1] != 1:
left += 1
while right - 1 >= 0 and goodChapter[right - 1] != 1:
right -= 1
current_length = sum(array_a[left:right + 1])
if current_length < min_length:
min_length = current_length
best_left, best_right = left, right
return f"{maxQualityCount},{best_left + 1},{best_right + 1}"
return f"{maxQualityCount},{left + 1},{right + 1}"
if __name__ == "__main__":
# You can add more test cases here
print(solution(8, 15000, [1000, 3000, 2000, 4000, 3000, 2000, 4000, 2000]) == "2,1,5" )
print(solution(8, 15000, [2000, 5000, 2000, 1000, 4000, 2000, 4000, 3000]) == "2,4,8" )