1. 问题背景与核心挑战
今天我想和大家分享一道非常有意思的LeetCode题目——730.统计不同回文子序列。这道题在动态规划问题中属于中等偏上难度,特别考验我们对区间DP和去重策略的理解。
首先明确题目要求:给定一个仅包含小写字母的字符串s,我们需要统计其中所有不同的非空回文子序列的数量。这里的"不同"意味着即使两个子序列在字符串中的位置不同,只要它们的字符组成相同,就视为同一个子序列。例如在字符串"bccb"中,使用第一个b和最后一个b组成的"bb",与使用第二个b和第三个b组成的"bb"只能算作一个。
这道题的核心挑战在于:
- 如何高效地统计所有可能的回文子序列
- 如何在统计过程中避免重复计数
- 如何处理大数取模的问题(结果需要对10^9+7取模)
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 动态规划解法思路解析
2.1 基础DP状态定义
我们采用区间动态规划(Interval DP)的方法来解决这个问题。定义dp[i][j]表示在子串s[i...j]范围内,不同的回文子序列的个数。
传统的区间DP解法(如LeetCode 516题)通常直接比较s[i]和s[j]是否相等来进行状态转移,但这种方法无法有效处理去重问题。因此我们需要采用一种更聪明的策略。
2.2 基于字符枚举的去重策略
观察到题目中字符集通常限定为{'a','b','c','d'}(最多4种字符),我们可以利用这个特性来设计去重策略:
对于每个区间[i,j],我们枚举所有可能的字符x(x ∈ {'a','b','c','d'})作为回文子序列的首尾字符。这样做的优势在于:
- 每种以x开头和结尾的回文子序列都会被独立统计
- 通过固定首尾字符,可以确保相同字符组成的子序列不会被重复计算
具体来说,对于每个字符x,我们需要:
- 找到x在区间[i,j]中第一次出现的位置L
- 找到x在区间[i,j]中最后一次出现的位置R
然后根据L和R的不同关系进行不同的处理。
3. 详细状态转移分析
3.1 字符x不存在于区间内
如果L > R(即字符x在区间[i,j]中不存在),那么它对dp[i][j]的贡献为0。这种情况很简单,我们直接跳过即可。
3.2 字符x在区间内只出现一次
如果L == R(即字符x在区间内只出现一次),那么它只能形
