LeetCode730. 统计不同回文子序列


LeetCode730. 统计不同回文子序列

题目描述

本题目来自LeetCode上的『730. 统计不同回文子序列』

给定一个字符串 s,返回 s 中不同的非空「回文子序列」个数 。

通过从 s 中删除 0 个或多个字符来获得子序列。

如果一个字符序列与它反转后的字符序列一致,那么它是「回文字符序列」。

如果有某个 i , 满足 ai != bi ,则两个序列 a1, a2, ...b1, b2, ... 不同。

注意:

  • 结果可能很大,你需要对 10^9 + 7 取模 。
示例1:

输入:s = ‘bccb’
输出:6
解释:6 个不同的非空回文子字符序列分别为:’b’, ‘c’, ‘bb’, ‘cc’, ‘bcb’, ‘bccb’。
注意:’bcb’ 虽然出现两次但仅计数一次。

示例2:

输入:s = ‘abcdabcdabcdabcdabcdabcdabcdabcddcbadcbadcbadcbadcbadcbadcbadcba’
输出:104860361
解释:共有 3104860382 个不同的非空回文子序列,104860361 对 10e9 + 7 取模后的值。

提示
  • 1 <= s.length <= 1000
  • s[i] 仅包含 'a', 'b', 'c''d'

题解

设 $dp[c][i][j]$ 为 首位字母为 $c$,且在字符串 $s[i…j]$ 中的回文序列的个数,分类讨论:

  • $s[i]=c\wedge s[j]=c$,此时任意字符串 $s[i+1…j-1]$ 的回文序列在两侧加上字符 $c$,都还是回文序列,除了以 $c$ 开头的字符串 $s[i+1…j-1]$ 之外,其他的字符串还多了 $cc$,$c$ 这两个回文序列,因此有:
    $$
    dp[c][i][j]=2+\sum\limits_{c_k\in C}dp[c_k][i+1][j-1]
    $$

  • $s[i]=c\wedge s[j]\ne c$,此时 $s[j]$ 不会对以 $c$ 为首尾的字符串包含的回文序列产生贡献,因此有:
    $$
    dp[c][i][j]=dp[c][i][j-1]
    $$

  • $s[i]\ne c\wedge s[j]= c$,此时 $s[i]$ 不会对以 $c$ 为首尾的字符串包含的回文序列产生贡献,因此有:
    $$
    dp[c][i][j]=dp[c][i+1][j]
    $$

  • $s[i]\ne c\wedge s[j]\ne c$,此时 $s[i]$ 和 $s[j]$ 都不会对以 $c$ 为首尾的字符串包含的回文序列产生贡献,因此有:
    $$
    dp[c][i][j]=dp[c][i+1][j-1]
    $$

考虑初始条件,当只有一个字符的时候,回文序列的个数为 $1$,即 $dp[c][i][i]=1$
注: 使用普通的递归会 TLE,Python在使用 @cache 开启记忆化递归后,可以通过,虽然还是很慢(3220 ms)

代码

class Solution {
private:
    static constexpr int MOD = 1e9 + 7;
public:
    int countPalindromicSubsequences(string s) {
        int n = s.size();
        int dp[4][1010][1010] = {0};
        for (int i = 0; i < n; ++i) {
            dp[s[i] - 'a'][i][i] = 1;
        }

        for (int len = 2; len <= n; ++len) {
            for (int i = 0, j = len - 1; j < n; ++i, ++j) {
                for (int k = 0; k < 4; ++k) {
                    char c = 'a' + k;
                    if (s[i] == c && s[j] == c) {
                        for (int ck = 0; ck < 4; ++ck) {
                            dp[k][i][j] += dp[ck][i + 1][j - 1];
                            dp[k][i][j] %= MOD;
                        }
                        dp[k][i][j] = (dp[k][i][j] + 2) % MOD;
                    } else if (s[i] == c && s[j] != c) {
                        dp[k][i][j] = dp[k][i][j - 1];
                    } else if (s[i] != c && s[j] == c) {
                        dp[k][i][j] = dp[k][i + 1][j];
                    } else {
                        dp[k][i][j] = dp[k][i + 1][j - 1];
                    }
                }
            }
        }

        int ans = 0;
        for (int i = 0; i < 4; ++i) {
            ans += dp[i][0][n - 1];
            ans %= MOD;
        }

        return ans;
    }
};

代码-记忆优化递归

class Solution:
    def countPalindromicSubsequences(self, s: str) -> int:
        MOD = int(1e9 + 7)

        @cache
        def dfs(c : str, i: int, j: int) -> int:
            if i >= j:
                return int(i == j and s[i] == c)
            if s[i] == c and s[j] == c:
                return (sum(dfs(ck, i + 1, j - 1) for ck in 'abcd') + 2) % MOD
            if s[i] == c:
                return dfs(c, i, j - 1)
            if s[j] == c:
                return dfs(c, i + 1, j)
            return dfs(c, i + 1, j - 1)

        return sum(dfs(c, 0, len(s) - 1) for c in 'abcd') % MOD

复杂度分析

  • 时间复杂度:$O(4n^2)$
  • 空间复杂度:$O(4n^2)$

文章作者: xitie2000
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 xitie2000 !
评论
  目录