小白都能看懂的“秋叶收藏集“解法

题目来源: Leetcode LCP 19. 秋叶收藏集

一.题目

小扣出去秋游,途中收集了一些红叶和黄叶,他利用这些叶子初步整理了一份秋叶收藏集 leaves, 字符串 leaves 仅包含小写字符 r 和 y, 其中字符 r 表示一片红叶,字符 y 表示一片黄叶。
出于美观整齐的考虑,小扣想要将收藏集中树叶的排列调整成「红、黄、红」三部分。每部分树叶数量可以不相等,但均需大于等于 1。每次调整操作,小扣可以将一片红叶替换成黄叶或者将一片黄叶替换成红叶。请问小扣最少需要多少次调整操作才能将秋叶收藏集调整完毕。

示例 1
输入:leaves = "rrryyyrryyyrr"
输出:2
解释:调整两次,将中间的两片红叶替换成黄叶,得到 "rrryyyyyyyyrr"

示例 2
输入:leaves = "ryr"
输出:0
解释:已符合要求,不需要额外操作

提示

  • 3 <= leaves.length <= 10^5
  • leaves 中只包含字符 'r' 和字符 'y'

二.解题思路

初看此题可以确定需要将字符串分为三段:

  • 阶段1r段,此段的黄叶都要替换为红叶
  • 阶段2y段,此段的红叶都要替换为黄叶
  • 阶段3r段,此段的黄叶都要替换为红叶

而题解即为三段替换次数总和的最小值,那么如何确定这个最小值呢?有分析可知,后一阶段的决策要取决于前一阶段,因此可以考虑使用动态规划。具体做法是定义一个 3 × l e n g t h 3\times{length} 3×length的数组 d p dp dp,其中 l e n g t h length length为字符串串长,结构示意图如下:
初始化状态

其中 d p [ i ] [ 0 ] , d p [ i ] [ 1 ] , d p [ i ] [ 2 ] dp[i][0],dp[i][1],dp[i][2] dp[i][0],dp[i][1],dp[i][2]的含义是第 i i i片叶子处于阶段 1 / 2 / 3 1/2/3 1/2/3时处理所需的最小步骤数,由此可知最终结果为 d p [ n − 1 ] [ 2 ] dp[n-1][2] dp[n1][2]。因此现在问题就转变为了确定 d p dp dp表各个阶段的值,步骤为:

  1. 确定阶段1
  2. 确定阶段2
  3. 确定阶段3

2.1 阶段一

首先,需要确定阶段1,对于该阶段需要先确认 d p [ 0 ] [ 0 ] dp[0][0] dp[0][0]的值,由题意可得字符串leaves的第一个字符必须为'r',因此可得:
d p [ 0 ] [ 0 ] = { 0 l e a v e s [ 0 ] = = ′ r ′ 1 l e a v e s [ 0 ] = = ′ y ′ dp[0][0]=\begin{cases} 0 & leaves[0] == 'r' \\ 1 & leaves[0]=='y' \\ \end{cases} dp[0][0]={ 01leaves[0]==rleaves[0]==y
而对于其他处于阶段1的元素 d p [ i ] [ 0 ] dp[i][0] dp[i][0],其递推计算公式为:
d p [ i ] [ 0 ] = { d p [ i − 1 ] [ 0 ] l e a v e s [ i ] = = ′ r ′ d p [ i − 1 ] [ 0 ] + 1 l e a v e s [ i ] = = ′ y ′ dp[i][0]=\begin{cases} dp[i-1][0] & leaves[i] =='r' \\ dp[i-1][0] + 1 & leaves[i]=='y' \\ \end{cases} dp[i][0]={ dp[i1][0]dp[i1][0]+1leaves[i]==rleaves[i]==y

2.2 阶段二

对于阶段2,其应从字符串leaves的第二个字符开始(前面至少一片红叶),其初值 d p [ 1 ] [ 1 ] dp[1][1] dp[1][1]的计算如下:
d p [ 1 ] [ 1 ] = { d p [ 0 ] [ 0 ] + 1 l e a v e s [ 1 ] = = ′ r ′ d p [ 0 ] [ 0 ] l e a v e s [ 1 ] = = ′ y ′ dp[1][1]=\begin{cases} dp[0][0] + 1& leaves[1] == 'r' \\ dp[0][0] & leaves[1]=='y' \\ \end{cases} dp[1][1]={ dp[0][0]+1dp[0][0]leaves[1]==rleaves[1]==y
而对于阶段2的其他元素 d p [ i ] [ 1 ] dp[i][1] dp[i][1],由于它的前一个字符可能来自阶段2,也可能来自阶段1,因此其递推公式如下:
d p [ i ] [ 1 ] = { m i n ( d p [ i − 1 ] [ 0 ] , d p [ i − 1 ] [ 1 ] ) + 1 l e a v e s [ i ] = = ′ r ′ m i n ( d p [ i − 1 ] [ 0 ] , d p [ i − 1 ] [ 1 ] ) l e a v e s [ i ] = = ′ y ′ dp[i][1]=\begin{cases} min(dp[i-1][0], dp[i-1][1]) + 1 & leaves[i] =='r' \\ min(dp[i-1][0], dp[i-1][1]) & leaves[i]=='y' \\ \end{cases} dp[i][1]={ min(dp[i1][0],dp[i1][1])+1min(dp[i1][0],dp[i1][1])leaves[i]==rleaves[i]==y

2.3 阶段三

对于阶段3,其应从字符串leaves的第三个字符开始(前面至少一红一黄),因此其初值 d p [ 2 ] [ 2 ] dp[2][2] dp[2][2]计算公式如下:
d p [ 2 ] [ 2 ] = { d p [ 1 ] [ 1 ] l e a v e s [ 2 ] = = ′ r ′ d p [ 1 ] [ 1 ] + 1 l e a v e s [ 2 ] = = ′ y ′ dp[2][2]=\begin{cases} dp[1][1] & leaves[2] == 'r' \\ dp[1][1] + 1& leaves[2]=='y' \\ \end{cases} dp[2][2]={ dp[1][1]dp[1][1]+1leaves[2]==rleaves[2]==y
而对于阶段2的其他元素 d p [ i ] [ 2 ] dp[i][2] dp[i][2],由于它的前一个字符可能来自阶段2,也可能来自阶段3,因此其递推公式如下:
d p [ i ] [ 2 ] = { m i n ( d p [ i − 1 ] [ 1 ] , d p [ i − 1 ] [ 2 ] ) l e a v e s [ i ] = = ′ r ′ m i n ( d p [ i − 1 ] [ 1 ] , d p [ i − 1 ] [ 2 ] ) + 1 l e a v e s [ i ] = = ′ y ′ dp[i][2]=\begin{cases} min(dp[i-1][1], dp[i-1][2]) & leaves[i] =='r' \\ min(dp[i-1][1], dp[i-1][2]) + 1 & leaves[i]=='y' \\ \end{cases} dp[i][2]={ min(dp[i1][1],dp[i1][2])min(dp[i1][1],dp[i1][2])+1leaves[i]==rleaves[i]==y

三.算法动态运行过程

这里以题目中的示例leaves = "rrryyyrryyyrr"为例进行动态演示,下面是全过程:

阶段1初值设定

阶段1初始化

阶段1其他值计算

阶段1其他值计算

阶段2初值设定

阶段2初始化

阶段2其他值计算

阶段2其他值计算

阶段3初值设定

阶段3初始化

阶段3其他值计算

阶段3其他值计算

四.示例代码

from typing import List

class Solution:
    def minimumOperations(self, leaves: str) -> int:
        n = len(leaves)
        dp = [[0, 0, 0] for _ in range(n)]
        #阶段1初值计算
        dp[0][0] = (1 if leaves[0] == 'y' else 0)
        #阶段1其他值计算
        for i in range(1, n):
            dp[i][0] = dp[i - 1][0] + (1 if leaves[i] == 'y' else 0)

        #阶段2初值计算
        dp[1][1] = dp[0][0] + (1 if leaves[1] == 'r' else 0)
        #阶段2其他值计算
        for i in range(2,n):
            dp[i][1] = min(dp[i-1][0],dp[i-1][1]) + (1 if leaves[i] == 'r' else 0)
        
        #阶段3初值计算
        dp[2][2] = dp[1][1] + (1 if leaves[2] == 'y' else 0)
        #阶段3其他值计算
        for i in range(3,n):
            dp[i][2] = min(dp[i-1][1],dp[i-1][2]) + (1 if leaves[i] == 'y' else 0)
        
        return dp[n - 1][2]

s = Solution()
leaves = "rrryyyrryyyrr"
print(s.minimumOperations(leaves))
"""
2
"""

以上便是本文的全部内容,如果觉得不错的话,可以点个赞或关注一下博主,后续将会持续更新Leetcode算法题题解,若是有错误的地方,也敬请批评指正!!!

猜你喜欢

转载自blog.csdn.net/qq_42103091/article/details/108562409