分割回文串 II
难度:
标签:
题目描述
给你一个字符串 s
,请你将 s
分割成一些子串,使每个子串都是回文串。
返回符合要求的 最少分割次数 。
示例 1:
输入:s = "aab" 输出:1 解释:只需一次分割就可将 s 分割成 ["aa","b"] 这样两个回文子串。
示例 2:
输入:s = "a" 输出:0
示例 3:
输入:s = "ab" 输出:1
提示:
1 <= s.length <= 2000
s
仅由小写英文字母组成
代码结果
运行时间: 40 ms, 内存: 0.0 MB
/*
* 题目思路:
* 1. 定义一个dp数组,其中dp[i]表示字符串s[0...i]的最少分割次数。
* 2. 初始化dp数组为最大值,除了dp[0]为0,因为单个字符本身就是回文串。
* 3. 使用Java Stream API实现内层循环,判断s[j...i]是否是回文串,并更新dp[i]。
* 4. 返回dp[n-1]的值,即字符串s的最少分割次数。
*/
import java.util.stream.IntStream;
public class PalindromePartitioningStream {
public int minCut(String s) {
int n = s.length();
boolean[][] isPalindrome = new boolean[n][n];
int[] dp = new int[n];
for (int i = 0; i < n; i++) {
final int finalI = i;
dp[i] = IntStream.rangeClosed(0, i).filter(j -> s.charAt(finalI) == s.charAt(j) && (finalI - j <= 2 || isPalindrome[j + 1][finalI - 1]))
.peek(j -> isPalindrome[j][finalI] = true)
.map(j -> j == 0 ? 0 : dp[j - 1] + 1)
.min().orElse(i);
}
return dp[n - 1];
}
}
解释
方法:
这个题解使用了动态规划的思路。首先判断整个字符串是否为回文串,如果是则直接返回0。然后判断是否存在一个分割点,使得分割成的两个子串都是回文串,如果存在则返回1。否则使用动态规划,定义 dp[i] 表示前i个字符组成的子串 s[0:i] 的最小分割次数。状态转移方程为:如果 s[l:r] 是回文串,那么 dp[r] = min(dp[r], dp[l]+1),其中 l 为 s[0:r] 的任意分割点。最后返回 dp[-1] 即为最小分割次数。
时间复杂度:
O(n^2)
空间复杂度:
O(n)
代码细节讲解
🦆
在动态规划中,dp数组的初始化为什么选择'-1'加上每个索引i的值,这样的初始化方式有什么特别的意义吗?
▷🦆
状态转移方程中,为什么dp[r + 1]的更新方式选择是min(dp[r + 1], dp[l] + 1),这样的更新逻辑是怎样推导出来的?
▷🦆
在检查s[l:r+1]为回文的循环条件中,为什么选择同时变动l和r,这种中心扩展法是如何确保能遍历到所有可能的回文子串的?
▷🦆
在实际代码中,如何理解并实现从某个字符为中心向两边扩展来检查回文的逻辑?
▷