线性动态规划:最长回文子串(动态规划解法)的进阶版——统计所有长度至少为L的不同回文子串的个数
字数 1671 2025-12-17 22:55:43

线性动态规划:最长回文子串(动态规划解法)的进阶版——统计所有长度至少为L的不同回文子串的个数

题目描述
给定一个字符串 s 和一个正整数 L,要求统计所有长度至少为 L不同回文子串的个数。这里的子串是连续的,且不同指的是子串内容不同(即相同的字符串只计数一次)。例如,对于 s = "ababa"L = 2,长度为2或以上的不同回文子串有 "aba""bab""ababa" 三个,所以答案是3。

解题过程
这是一个字符串处理问题,结合了回文串判断和去重计数。我们需要高效地找出所有长度至少为 L 的回文子串,并去重。动态规划可以用来判断任意子串是否为回文,同时配合哈希集合(如 Python 的 set)来去重。

步骤分解:

  1. 定义状态
    dp[i][j] 表示字符串 s 从索引 ij 的子串(包含两端)是否为回文串。dp[i][j] 为布尔值:True 表示是回文,False 表示不是。

    • 如果 s[i] == s[j],且去掉两端的子串 s[i+1:j-1] 是回文(或长度为0/1),则整个子串是回文。
    • 转移方程:
      dp[i][j] = (s[i] == s[j]) and (j - i <= 2 or dp[i+1][j-1])
      其中 j - i <= 2 表示子串长度为1、2或3时,只要两端字符相等就一定是回文。
  2. 初始化与填表顺序
    因为 dp[i][j] 依赖于 dp[i+1][j-1](左下角的状态),我们需要按子串长度从小到大进行递推。

    • 外层循环枚举子串长度 len,从 1n(n为字符串长度)。
    • 内层循环枚举起始位置 i,计算结束位置 j = i + len - 1
    • 这样保证计算 dp[i][j] 时,dp[i+1][j-1] 已经计算过了。
  3. 收集结果并去重
    在计算过程中,每当发现一个回文子串 s[i:j+1](Python切片是左闭右开,所以是 s[i:j+1]),检查其长度是否至少为 L。如果是,则将其加入一个集合(set)中,集合自动去重。

  4. 输出结果
    最后,集合的大小就是不同回文子串的个数。

举例说明
s = "ababa", L = 2 为例:

  • 字符串长度 n = 5
  • 枚举所有子串:
    • 长度=2:"ab" 不是回文,"ba" 不是,"ab" 不是,"ba" 不是。
    • 长度=3:"aba" 是回文(长度≥2,加入集合),"bab" 是(加入),"aba" 是(重复,不重复加入)。
    • 长度=4:"abab" 不是,"baba" 不是。
    • 长度=5:"ababa" 是回文(加入集合)。
  • 集合中有 {"aba", "bab", "ababa"},共3个。

复杂度分析

  • 时间复杂度:动态规划填表需要 O(n²),每次子串加入集合(字符串切片)的代价为 O(子串长度),最坏情况下所有子串都是回文(如全相同字符),总共有 O(n²) 个子串,每个子串长度平均 O(n),所以最坏为 O(n³)。但实际中回文子串数量远少于全部子串,且可以通过存储子串的哈希值(如通过字符串哈希)优化到 O(n²)。
  • 空间复杂度:O(n²) 用于存储 dp 表,以及集合存储最多 O(n²) 个子串(每个子串长度 O(n)),所以空间最坏 O(n³)(如果存储完整子串)。同样可以用哈希值优化到 O(n²)。

优化思路
如果需要处理更长的字符串,可以采用中心扩展法枚举所有回文子串(每个中心点向两边扩展),配合哈希集合去重,时间复杂度为 O(n²),空间为 O(n²)(用于存储哈希值)。不过动态规划解法更直接,易于理解。

总结
这个题目是最长回文子串问题的自然扩展,要求统计所有满足长度限制的不同回文子串。通过动态规划判断所有子串的回文性,并用集合去重,即可得到答案。

线性动态规划:最长回文子串(动态规划解法)的进阶版——统计所有长度至少为L的不同回文子串的个数 题目描述 给定一个字符串 s 和一个正整数 L ,要求统计所有长度至少为 L 的 不同 回文子串的个数。这里的子串是连续的,且不同指的是子串内容不同(即相同的字符串只计数一次)。例如,对于 s = "ababa" 和 L = 2 ,长度为2或以上的不同回文子串有 "aba" 、 "bab" 、 "ababa" 三个,所以答案是3。 解题过程 这是一个字符串处理问题,结合了回文串判断和去重计数。我们需要高效地找出所有长度至少为 L 的回文子串,并去重。动态规划可以用来判断任意子串是否为回文,同时配合哈希集合(如 Python 的 set )来去重。 步骤分解: 定义状态 设 dp[i][j] 表示字符串 s 从索引 i 到 j 的子串(包含两端)是否为回文串。 dp[i][j] 为布尔值: True 表示是回文, False 表示不是。 如果 s[i] == s[j] ,且去掉两端的子串 s[i+1:j-1] 是回文(或长度为0/1),则整个子串是回文。 转移方程: dp[i][j] = (s[i] == s[j]) and (j - i <= 2 or dp[i+1][j-1]) 其中 j - i <= 2 表示子串长度为1、2或3时,只要两端字符相等就一定是回文。 初始化与填表顺序 因为 dp[i][j] 依赖于 dp[i+1][j-1] (左下角的状态),我们需要按子串长度从小到大进行递推。 外层循环枚举子串长度 len ,从 1 到 n (n为字符串长度)。 内层循环枚举起始位置 i ,计算结束位置 j = i + len - 1 。 这样保证计算 dp[i][j] 时, dp[i+1][j-1] 已经计算过了。 收集结果并去重 在计算过程中,每当发现一个回文子串 s[i:j+1] (Python切片是左闭右开,所以是 s[i:j+1] ),检查其长度是否至少为 L 。如果是,则将其加入一个集合( set )中,集合自动去重。 输出结果 最后,集合的大小就是不同回文子串的个数。 举例说明 以 s = "ababa" , L = 2 为例: 字符串长度 n = 5 。 枚举所有子串: 长度=2: "ab" 不是回文, "ba" 不是, "ab" 不是, "ba" 不是。 长度=3: "aba" 是回文(长度≥2,加入集合), "bab" 是(加入), "aba" 是(重复,不重复加入)。 长度=4: "abab" 不是, "baba" 不是。 长度=5: "ababa" 是回文(加入集合)。 集合中有 {"aba", "bab", "ababa"} ,共3个。 复杂度分析 时间复杂度:动态规划填表需要 O(n²),每次子串加入集合(字符串切片)的代价为 O(子串长度),最坏情况下所有子串都是回文(如全相同字符),总共有 O(n²) 个子串,每个子串长度平均 O(n),所以最坏为 O(n³)。但实际中回文子串数量远少于全部子串,且可以通过存储子串的哈希值(如通过字符串哈希)优化到 O(n²)。 空间复杂度:O(n²) 用于存储 dp 表,以及集合存储最多 O(n²) 个子串(每个子串长度 O(n)),所以空间最坏 O(n³)(如果存储完整子串)。同样可以用哈希值优化到 O(n²)。 优化思路 如果需要处理更长的字符串,可以采用 中心扩展法 枚举所有回文子串(每个中心点向两边扩展),配合哈希集合去重,时间复杂度为 O(n²),空间为 O(n²)(用于存储哈希值)。不过动态规划解法更直接,易于理解。 总结 这个题目是 最长回文子串 问题的自然扩展,要求统计所有满足长度限制的不同回文子串。通过动态规划判断所有子串的回文性,并用集合去重,即可得到答案。