优质章节的连续选择 | 豆包Marscode AI刷题

115 阅读7分钟

使用滑动窗口解决,需要解决的问题有

  1. 如何初始化left 与 right?
  2. 如何控制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" )