Leetcode1278:分割回文串 III

题目描述:

给你一个由小写字母组成的字符串 s,和一个整数 k

请你按下面的要求分割字符串:

  • 首先,你可以将 s 中的部分字符修改为其他的小写英文字母。
  • 接着,你需要把 s 分割成 k 个非空且不相交的子串,并且每个子串都是回文串。

请返回以这种方式分割字符串所需修改的最少字符数。

代码思路:

  1. 动态规划数组 dp 的初始化
    • dp[i][j] 表示将字符串 s 的子串 s[i:j+1] 变为回文串所需的最少字符修改次数。
    • 初始化一个 L x L 的二维数组 dp,其中 L 是字符串 s 的长度。
    • 对于任意 i == j 的情况,即子串只包含一个字符时,这个字符自然是回文的,所以 dp[i][j] = 0
  2. 填充动态规划数组 dp
    • 使用双重循环遍历字符串 s 的所有子串。
    • 计算 v = int(s[i] != s[j]),表示如果 s[i] 和 s[j] 不相等,则需要修改的次数为 1;否则为 0。
    • 对于长度小于 3 的子串,直接根据两端的字符是否相等来设置 dp[i][j] 的值。
    • 对于长度大于等于 3 的子串,使用状态转移方程 dp[i][j] = dp[i + 1][j - 1] + v 来计算最少修改次数。
  3. 记忆化搜索函数 dfs
    • 定义函数 dfs(idx, sur),返回将字符串 s 从索引 idx 开始的部分分割成 sur 个回文子串所需的最少字符修改次数。
    • 基础情况:当 sur == 1 时,返回 dp[idx][L-1],即将当前剩余部分变为一个回文串所需的最少修改次数。
    • 使用循环枚举所有可能的分割点,并计算当前分割方案下的最少修改次数。取所有可能方案中的最小值作为结果。
  4. 返回结果
    • 调用 dfs(0, k),返回从字符串 s 的起始位置开始,分割成 k 个回文子串所需的最少字符修改次数。

代码实现: 

class Solution:
    def palindromePartition(self, s: str, k: int) -> int:
        L = len(s)
        # 定义dp数组, 其中dp[i][j]为s[i:j + 1]变为回文串需要修改的的最少字符数
        # 当i == j时, dp[i][j] = 0 (单个字符必然是回文的)
        dp = [[0] * L for i in range(L)]
        # 转移方程: dp[i][j] = dp[i + 1][j - 1] + (s[i] != s[j]) (参考最长回文子串)

        for j in range(L):
            for i in range(j):
                v = int(s[i] != s[j])
                if j - i < 3:
                    dp[i][j] = v
                else:
                    dp[i][j] = dp[i + 1][j - 1] + v
        # 这样我们得到了一个辅助数组,可以快速查询s的某个子串变为回文串需要修改的的最少字符数

        # 记忆化搜索
        # 定义函数dfs(idx, sur): 返回需要分割sur个字符串时, s[idx:]所需修改的最少字符数。
        @cache
        def dfs(idx, sur):
            # base case: 当只需要分割成1个子串时,只能把当前字符串变为回文串
            if sur == 1: return dp[idx][L - 1] 
            ans = inf
            # 枚举分割点取最小值即可, 分割点不能太靠后, 要始终保证s[idx:]的字符数量大于等于sur
            # 不然根本无法分割成sur个子串
            for i in range(idx, L - sur + 1):
                ans = min(ans, dp[idx][i] + dfs(i + 1, sur - 1))
            return ans
        
        return dfs(0, k)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值