leetcode
leetcode 2151 ~ 2200
不重叠回文子字符串的最大数目

不重叠回文子字符串的最大数目

难度:

标签:

题目描述

代码结果

运行时间: 43 ms, 内存: 16.1 MB


/*
* 思路:
* 1. 仍然使用动态规划思想,但结合Java Stream处理。
* 2. dp数组的定义和java解法一样。
* 3. 使用Stream和Lambda表达式简化代码。
*/
import java.util.stream.IntStream;
public class Solution {
    public int maxPalindromes(String s, int k) {
        int n = s.length();
        int[] dp = new int[n + 1];
        IntStream.range(0, n).forEach(i -> {
            dp[i + 1] = dp[i];
            IntStream.rangeClosed(0, i - k + 1).filter(j -> isPalindrome(s, j, i)).forEach(j -> dp[i + 1] = Math.max(dp[i + 1], dp[j] + 1));
        });
        return dp[n];
    }

    private boolean isPalindrome(String s, int left, int right) {
        while (left < right) {
            if (s.charAt(left++) != s.charAt(right--)) {
                return false;
            }
        }
        return true;
    }
}

解释

方法:

这个题解采用了动态规划的方法与中心扩展算法的结合。对于每个中心点(字符或两字符之间),算法尝试向两边扩展以找到可能的回文串。动态规划数组 `f` 中,`f[i]` 表示考虑到字符串的前 `i` 个字符,能够形成的最大不重叠的回文串个数。每次找到一个合法的回文串后(长度至少为 `k`),就尝试更新 `f[r+1]`(即扩展到的右边界的下一个位置)。这样,当遍历完成所有可能的中心点后,`f[n]` 将包含整个字符串的最大不重叠回文子字符串数目。

时间复杂度:

O(n^2)

空间复杂度:

O(n)

代码细节讲解

🦆
为什么在找到一个长度至少为k的回文串后,就不再继续扩展,直接break退出循环?
在找到一个长度至少为k的回文串后,break的原因是为了避免重叠的情况。一旦找到满足条件的回文字符串,如果继续扩展可能会导致与其他已经确认的回文串重叠,从而违反题目的不重叠条件。因此,在找到第一个满足条件的回文串后,就立即停止扩展,确保此后每次更新的回文子串都是不重叠的。
🦆
在动态规划数组f中,为什么要初始化f[l+1]为max(f[l+1], f[l])?这样的初始化有什么作用?
在动态规划数组f中,初始化f[l+1]为max(f[l+1], f[l])的作用是保证f数组的单调性和正确性。这样的初始化确保了在考虑前l个字符的基础上,到第l+1个字符时,最大不重叠回文子字符串的数量至少不会减少。这种方式可以有效地继承前一个状态的值,确保不会因为后续操作而错过之前已经得到的最优解。
🦆
动态规划解法中,如何确保更新的回文子串是不重叠的?
在动态规划解法中,确保更新的回文子串不重叠的机制是通过仔细控制数组f的更新逻辑来实现的。每次在找到一个合法的回文串后,更新f[r+1](即回文串右边界的下一个位置)为max(f[r+1], f[l] + 1)。这样的更新方式确保了在第l个字符之前的最大不重叠回文串数加上当前新找到的回文串,而更新的位置r+1表示从下一个位置开始重新计算,避免了与当前回文串的重叠。
🦆
在中心扩展算法中,为什么要选择2n-1个中心点?具体是如何确定每个中心点的左右边界l和r的?
在中心扩展算法中,选择2n-1个中心点是为了覆盖所有可能的回文中心,包括字符本身和两个字符之间的位置。对于偶数长度的回文串,中心点位于两个字符之间;对于奇数长度的回文串,中心点位于某个字符上。具体地,中心点的计算公式为i // 2来确定中心点左侧l,而中心点右侧r则由i // 2 + i % 2确定。这样的设置可以确保每个可能的回文中心都被考虑到,从而不遗漏任何一个回文串的可能性。

相关问题