leetcode-统计不同回文子序列

210 阅读2分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第16天,点击查看活动详情

题目描述

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

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

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

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

注意:
结果可能很大,你需要对 109 + 7 取模 。
 

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

示例 2:
输入:s = 'abcdabcdabcdabcdabcdabcdabcdabcddcbadcbadcbadcbadcbadcbadcbadcba'
输出:104860361
解释:共有 3104860382 个不同的非空回文子序列,104860361 对 109 + 7 取模后的值。
 

提示:

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

思路

听说过区间DP,但是还是没写出来状态转移方程,看了官方题解和三叶姐的题解,记录一下。
定义二维数组f[][],f[i][j]代表下标在[i,j]之间的子串的不同回文个数。 可选字符只有abcd4个,可以依次枚举,计算每个字符作为边缘时对f[i][j]的贡献度,有以下几种情况:

  • f[i][j]不包含字符ch,那么其中任何一个回文子串都不会包含ch,贡献度为0
  • f[i][j]只包含字符ch1次,只有[ch]本身是回文,那么贡献度为1
  • f[i][j]包含字符ch的次数大于等于2次,那么把ch在f[i][j]中最左和最右的位置记为left和right,
    • 如果left+1==right,那么贡献度有[ch]、[chch],即2
    • 如果left+1<right,那么贡献度有[ch]、[chch]、f[left+1][right-1],即 f[left+1][right-1] + 2

Java版本代码

class Solution {

    private static final int MOD = 1000000007;

    public int countPalindromicSubsequences(String s) {
        final int CHAR_COUNT = 4;
        char[] cs = s.toCharArray();
        int n = cs.length;
        int[][] f = new int[n][n];
        int[] leftIndex = new int[CHAR_COUNT];
        int[] rightIndex = new int[CHAR_COUNT];
        Arrays.fill(leftIndex, -1);
        for (int i = n-1; i >= 0; i--) {
            leftIndex[cs[i] - 'a'] = i;
            Arrays.fill(rightIndex, -1);
            for (int j = i; j < n; j++) {
                rightIndex[cs[j] - 'a'] = j;
                for (int c = 0; c < CHAR_COUNT; c++) {
                    int left = leftIndex[c], right = rightIndex[c];
                    if (left == -1 || right == -1) {
                        continue;
                    }
                    if (left == right) {
                        f[i][j] = (f[i][j] + 1) % MOD;
                    } else if (left+1 == right) {
                        f[i][j] = (f[i][j] + 2) % MOD;
                    } else {
                        f[i][j] = (f[i][j] + f[left+1][right-1] + 2) % MOD;
                    }
                }
            }
        }
        return f[0][n-1];
    }
}