【LetMeFly】132.分割回文串 II:动态规划
力扣题目链接:https://leetcode.cn/problems/palindrome-partitioning-ii/
给你一个字符串 s
,请你将 s
分割成一些子串,使每个子串都是回文串。
返回符合要求的 最少分割次数 。
示例 1:
输入:s = "aab"
输出:1
解释:只需一次分割就可将 s 分割成 ["aa","b"] 这样两个回文子串。
示例 2:
输入:s = "a"
输出:0
示例 3:
输入:s = "ab"
输出:1
提示:
1 <= s.length <= 2000
s
仅由小写英文字母组成
解题方法:动态规划
整个过程分为两步:预处理 和 动态规划
动态规划:
使用数组$dp$,其中$dp[i]$代表使得子字符串$0…i$为回文字符串组合的最小分割次数,那么$dp[len(s) - 1]$即为答案。
预处理:
有没有什么办法$O(1)$时间内快速判断下标从$i$到$j$的子字符串是否为回文字符串?有,我们可以先使用$O(n^2)$复杂度的时间预处理。使用$isOk[i][j]$表示子字符串$i…j$是否为回文字符串:
- 如果子字符串为空或者长度为1,则是回文字符串($i \geq j$)
- 否则:是回文字符串当且仅当$s[i] == s[j] \text{ AND }isOk[i + 1][j - 1]$
时空复杂度分析
- 时间复杂度$O(n^2)$,预处理和动态规划的时间复杂度都是$O(n^2)$。其中$n = len(s)$
- 空间复杂度$O(n^2)$
AC代码
C++
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
|
class Solution { public: int minCut(string s) { vector<vector<bool>> isOk(s.size(), vector<bool>(s.size(), true)); for (int i = s.size() - 1; i >= 0; i--) { for (int j = i + 1; j < s.size(); j++) { isOk[i][j] = s[i] == s[j] && isOk[i + 1][j - 1]; } }
vector<int> dp(s.size(), 1000000); for (int i = 0; i < s.size(); i++) { if (isOk[0][i]) { dp[i] = 0; continue; } for (int j = 0; j < i; j++) { if (isOk[j + 1][i]) { dp[i] = min(dp[i], dp[j] + 1); } } } return dp.back(); } };
|
Python
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| ''' Author: LetMeFly Date: 2025-03-02 12:26:57 LastEditors: LetMeFly.xyz LastEditTime: 2025-03-02 12:33:40 ''' class Solution: def minCut(self, s: str) -> int: isOk = [[True] * len(s) for _ in range(len(s))] for i in range(len(s) - 1, -1, -1): for j in range(i + 1, len(s)): isOk[i][j] = s[i] == s[j] and isOk[i + 1][j - 1] dp = [100000] * len(s) for i in range(len(s)): if isOk[0][i]: dp[i] = 0 continue for j in range(i): if isOk[j + 1][i]: dp[i] = min(dp[i], dp[j] + 1) return dp[-1]
|
Java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
|
class Solution { public int minCut(String s) { boolean[][] isOk = new boolean[s.length()][s.length()]; for (int i = 0; i < s.length(); i++) { for (int j = 0; j < s.length(); j++) { isOk[i][j] = true; } } for (int i = s.length() - 1; i >= 0; i--) { for (int j = i + 1; j < s.length(); j++) { isOk[i][j] = s.charAt(i) == s.charAt(j) && isOk[i + 1][j - 1]; } }
int[] dp = new int[s.length()]; for (int i = 0; i < s.length(); i++) { dp[i] = 100000; } for (int i = 0; i < s.length(); i++) { if (isOk[0][i]) { dp[i] = 0; continue; } for (int j = 0; j < i; j++) { if (isOk[j + 1][i]) { dp[i] = Math.min(dp[i], dp[j] + 1); } } } return dp[dp.length - 1]; } }
|
Go
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
|
package main
func minCut(s string) int { isOk := make([][]bool, len(s)) for i, _ := range isOk { isOk[i] = make([]bool, len(s)) for j, _ := range isOk[i] { isOk[i][j] = true } } for i := len(s) - 1; i >= 0; i-- { for j := i + 1; j < len(s); j++ { isOk[i][j] = s[i] == s[j] && isOk[i + 1][j - 1] } }
dp := make([]int, len(s)) for i, _ := range dp { dp[i] = 100000 } for i := 0; i < len(dp); i++ { if isOk[0][i] { dp[i] = 0 continue } for j := 0; j < i; j++ { if isOk[j + 1][i] { dp[i] = min(dp[i], dp[j] + 1) } } } return dp[len(dp) - 1] }
|
同步发文于CSDN和我的个人博客,原创不易,转载经作者同意后请附上原文链接哦~
千篇源码题解已开源