最长回文子序列的变种:统计所有回文子序列的个数(进阶版:考虑字符集限制)
字数 1178 2025-11-17 22:37:06

最长回文子序列的变种:统计所有回文子序列的个数(进阶版:考虑字符集限制)

题目描述:
给定一个字符串s,统计其中所有非空回文子序列的个数。注意,即使两个回文子序列由相同字符组成但位置不同,也被视为不同的子序列。此外,题目还增加了字符集限制:字符串s只包含小写英文字母。

解题过程:

  1. 问题分析:
  • 我们需要统计字符串中所有回文子序列的数量
  • 回文子序列是指从原字符串中删除0个或多个字符后,剩余字符按原顺序组成的回文字符串
  • 由于是子序列而不是子串,字符可以不连续
  • 需要考虑字符集限制(小写英文字母)
  1. 动态规划定义:
    定义dp[i][j]表示字符串s从索引i到j(包含两端)的子串中,回文子序列的个数。

  2. 状态转移方程:
    考虑区间[i, j]:

  • 当i > j时,dp[i][j] = 0(空区间)
  • 当i = j时,dp[i][j] = 1(单个字符本身就是一个回文子序列)
  • 当s[i] ≠ s[j]时:
    dp[i][j] = dp[i+1][j] + dp[i][j-1] - dp[i+1][j-1]
    解释:用区间[i+1,j]和[i,j-1]的回文子序列数相加,但减去重复计算的区间[i+1,j-1]
  • 当s[i] = s[j]时:
    dp[i][j] = dp[i+1][j] + dp[i][j-1] - dp[i+1][j-1] + (dp[i+1][j-1] + 1)
    = dp[i+1][j] + dp[i][j-1] + 1
    解释:当两端字符相同时,除了包含之前的所有情况外,还能以s[i]和s[j]作为新的回文子序列的两端,与区间[i+1,j-1]内的所有回文子序列组合形成新的回文子序列,再加上s[i]s[j]本身这个长度为2的回文子序列
  1. 边界条件:
  • 对于所有i > j,dp[i][j] = 0
  • 对于所有i = j,dp[i][j] = 1
  1. 计算顺序:
    由于dp[i][j]依赖于dp[i+1][j]、dp[i][j-1]和dp[i+1][j-1],我们需要按照子串长度从小到大的顺序计算:
  • 先计算所有长度为1的子串
  • 然后计算长度为2的子串
  • 依此类推,直到计算整个字符串
  1. 最终结果:
    dp[0][n-1]就是整个字符串中所有回文子序列的数量

  2. 考虑字符集限制的优化:
    由于字符串只包含小写英文字母,我们可以利用这个特性进行优化:

  • 预处理每个字符的出现位置
  • 在计算过程中,可以利用字符的有限性来优化重复计算
  1. 算法实现:
def countPalindromicSubsequences(s: str) -> int:
    n = len(s)
    MOD = 10**9 + 7
    dp = [[0] * n for _ in range(n)]
    
    # 初始化长度为1的子串
    for i in range(n):
        dp[i][i] = 1
    
    # 按长度递增计算
    for length in range(2, n + 1):
        for i in range(n - length + 1):
            j = i + length - 1
            
            if s[i] == s[j]:
                dp[i][j] = (dp[i+1][j] + dp[i][j-1] + 1) % MOD
            else:
                dp[i][j] = (dp[i+1][j] + dp[i][j-1] - dp[i+1][j-1]) % MOD
    
    return dp[0][n-1]
  1. 复杂度分析:
  • 时间复杂度:O(n²),需要填充n×n的DP表
  • 空间复杂度:O(n²),用于存储DP表

这个解法能够准确统计字符串中所有回文子序列的数量,同时考虑了字符集限制带来的优化可能性。

最长回文子序列的变种:统计所有回文子序列的个数(进阶版:考虑字符集限制) 题目描述: 给定一个字符串s,统计其中所有非空回文子序列的个数。注意,即使两个回文子序列由相同字符组成但位置不同,也被视为不同的子序列。此外,题目还增加了字符集限制:字符串s只包含小写英文字母。 解题过程: 问题分析: 我们需要统计字符串中所有回文子序列的数量 回文子序列是指从原字符串中删除0个或多个字符后,剩余字符按原顺序组成的回文字符串 由于是子序列而不是子串,字符可以不连续 需要考虑字符集限制(小写英文字母) 动态规划定义: 定义dp[ i][ j ]表示字符串s从索引i到j(包含两端)的子串中,回文子序列的个数。 状态转移方程: 考虑区间[ i, j ]: 当i > j时,dp[ i][ j ] = 0(空区间) 当i = j时,dp[ i][ j ] = 1(单个字符本身就是一个回文子序列) 当s[ i] ≠ s[ j ]时: dp[ i][ j] = dp[ i+1][ j] + dp[ i][ j-1] - dp[ i+1][ j-1 ] 解释:用区间[ i+1,j]和[ i,j-1]的回文子序列数相加,但减去重复计算的区间[ i+1,j-1 ] 当s[ i] = s[ j ]时: dp[ i][ j] = dp[ i+1][ j] + dp[ i][ j-1] - dp[ i+1][ j-1] + (dp[ i+1][ j-1 ] + 1) = dp[ i+1][ j] + dp[ i][ j-1 ] + 1 解释:当两端字符相同时,除了包含之前的所有情况外,还能以s[ i]和s[ j]作为新的回文子序列的两端,与区间[ i+1,j-1]内的所有回文子序列组合形成新的回文子序列,再加上s[ i]s[ j ]本身这个长度为2的回文子序列 边界条件: 对于所有i > j,dp[ i][ j ] = 0 对于所有i = j,dp[ i][ j ] = 1 计算顺序: 由于dp[ i][ j]依赖于dp[ i+1][ j]、dp[ i][ j-1]和dp[ i+1][ j-1 ],我们需要按照子串长度从小到大的顺序计算: 先计算所有长度为1的子串 然后计算长度为2的子串 依此类推,直到计算整个字符串 最终结果: dp[ 0][ n-1 ]就是整个字符串中所有回文子序列的数量 考虑字符集限制的优化: 由于字符串只包含小写英文字母,我们可以利用这个特性进行优化: 预处理每个字符的出现位置 在计算过程中,可以利用字符的有限性来优化重复计算 算法实现: 复杂度分析: 时间复杂度:O(n²),需要填充n×n的DP表 空间复杂度:O(n²),用于存储DP表 这个解法能够准确统计字符串中所有回文子序列的数量,同时考虑了字符集限制带来的优化可能性。