0
点赞
收藏
分享

微信扫一扫

730. 统计不同回文子序列 : 枚举边缘字符的区间 DP 运用题

题目描述

这是 LeetCode 上的 ​​730. 统计不同回文子序列 ,难度为 困难

Tag : 「区间 DP」、「动态规划」

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

通过从 ​​s​​​ 中删除 730. 统计不同回文子序列 : 枚举边缘字符的区间 DP 运用题_回文串 个或多个字符来获得子序列。

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

如果有某个 730. 统计不同回文子序列 : 枚举边缘字符的区间 DP 运用题_后端_02 , 满足 730. 统计不同回文子序列 : 枚举边缘字符的区间 DP 运用题_算法_03 != 730. 统计不同回文子序列 : 枚举边缘字符的区间 DP 运用题_Java_04 ,则两个序列 ​​​a1, a2, ...​​​ 和 ​​b1, b2, ...​​ 不同。

注意:

  • 结果可能很大,你需要对730. 统计不同回文子序列 : 枚举边缘字符的区间 DP 运用题_子序列_05取模 。

示例 1:

输入:s = 'bccb'

输出:6

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

示例 2:

输入:s = 'abcdabcdabcdabcdabcdabcdabcdabcddcbadcbadcbadcbadcbadcbadcbadcba'

输出:104860361

解释:共有 3104860382 个不同的非空回文子序列,104860361 对 109 + 7 取模后的值。

提示:

  • 730. 统计不同回文子序列 : 枚举边缘字符的区间 DP 运用题_Java_06
  • ​s[i]​​​ 仅包含​​'a'​​​,​​'b'​​​,​​'c'​​​ 或​​'d'​

区间 DP

往长度较少的回文串两端添加字符,可能组成新的长度大的回文串,容易想到「区间 DP」,同时 ​​s​​​ 仅由 730. 统计不同回文子序列 : 枚举边缘字符的区间 DP 运用题_后端_07 类小写字母组成,也是一个切入点。

根据区间 DP 的一般思路,定义 730. 统计不同回文子序列 : 枚举边缘字符的区间 DP 运用题_后端_08 为考虑字符串 ​​​s​​​ 中的 730. 统计不同回文子序列 : 枚举边缘字符的区间 DP 运用题_子序列_09 范围内回文子序列的个数,最终答案为 730. 统计不同回文子序列 : 枚举边缘字符的区间 DP 运用题_回文串_10

不失一般性考虑 730. 统计不同回文子序列 : 枚举边缘字符的区间 DP 运用题_后端_08 该如何转移,通过枚举 ​​abcd​​ 作为回文方案「边缘字符」来进行统计,即分别统计各类字符作为「边缘字符」时对 730. 统计不同回文子序列 : 枚举边缘字符的区间 DP 运用题_后端_08 的贡献,此类统计方式天生不存在重复性问题。

假设当前枚举到的字符为 730. 统计不同回文子序列 : 枚举边缘字符的区间 DP 运用题_Java_13

  • 730. 统计不同回文子序列 : 枚举边缘字符的区间 DP 运用题_算法_14中没有字符730. 统计不同回文子序列 : 枚举边缘字符的区间 DP 运用题_Java_13,则字符730. 统计不同回文子序列 : 枚举边缘字符的区间 DP 运用题_Java_13730. 统计不同回文子序列 : 枚举边缘字符的区间 DP 运用题_后端_08贡献为730. 统计不同回文子序列 : 枚举边缘字符的区间 DP 运用题_回文串,跳过;
  • 730. 统计不同回文子序列 : 枚举边缘字符的区间 DP 运用题_算法_14中存在字符730. 统计不同回文子序列 : 枚举边缘字符的区间 DP 运用题_Java_13,根据字符730. 统计不同回文子序列 : 枚举边缘字符的区间 DP 运用题_Java_13在范围730. 统计不同回文子序列 : 枚举边缘字符的区间 DP 运用题_算法_14中「最小下标」和「最大下标」进行分情况讨论,假设字符730. 统计不同回文子序列 : 枚举边缘字符的区间 DP 运用题_Java_13730. 统计不同回文子序列 : 枚举边缘字符的区间 DP 运用题_算法_14中「最靠左」的位置为730. 统计不同回文子序列 : 枚举边缘字符的区间 DP 运用题_回文串_25,「最靠右」的位置为730. 统计不同回文子序列 : 枚举边缘字符的区间 DP 运用题_算法_26
  • 730. 统计不同回文子序列 : 枚举边缘字符的区间 DP 运用题_后端_27时,此时字符730. 统计不同回文子序列 : 枚举边缘字符的区间 DP 运用题_Java_13730. 统计不同回文子序列 : 枚举边缘字符的区间 DP 运用题_后端_08的贡献为730. 统计不同回文子序列 : 枚举边缘字符的区间 DP 运用题_子序列_30,即​​​k​​ 本身;
  • 730. 统计不同回文子序列 : 枚举边缘字符的区间 DP 运用题_算法_31时,说明字符730. 统计不同回文子序列 : 枚举边缘字符的区间 DP 运用题_Java_13中间不存在任何字符,此时字符730. 统计不同回文子序列 : 枚举边缘字符的区间 DP 运用题_Java_13730. 统计不同回文子序列 : 枚举边缘字符的区间 DP 运用题_后端_08的贡献为730. 统计不同回文子序列 : 枚举边缘字符的区间 DP 运用题_Java_35,包括​​​k​​​ 和​​kk​​ 两种回文方案;
  • 其余情况,可根据已算得的「小区间回文方案」进行延伸(两段分别补充位于730. 统计不同回文子序列 : 枚举边缘字符的区间 DP 运用题_回文串_25730. 统计不同回文子序列 : 枚举边缘字符的区间 DP 运用题_算法_26的字符730. 统计不同回文子序列 : 枚举边缘字符的区间 DP 运用题_Java_13),得到新的大区间方案,此部分对730. 统计不同回文子序列 : 枚举边缘字符的区间 DP 运用题_后端_08的贡献是730. 统计不同回文子序列 : 枚举边缘字符的区间 DP 运用题_后端_40,另外还有​​​k​​​ 和​​kk​​​ 两种回文方案,因此总的对答案的贡献为730. 统计不同回文子序列 : 枚举边缘字符的区间 DP 运用题_回文串_41

统计 730. 统计不同回文子序列 : 枚举边缘字符的区间 DP 运用题_算法_14 中各类字符「最靠左」和「最靠右」的位置,可通过调整枚举方向来实现:从大到小枚举 730. 统计不同回文子序列 : 枚举边缘字符的区间 DP 运用题_后端_02,同时维护 ​​​L[s[i]-'a'] = i​​​,即可得到「最靠左」的位置;在确定左端点 730. 统计不同回文子序列 : 枚举边缘字符的区间 DP 运用题_后端_02 之后,从小到达枚举右端点 730. 统计不同回文子序列 : 枚举边缘字符的区间 DP 运用题_后端_45,同时维护 ​​​R[s[i]-'a'] = j​​,即可得到「最靠右」的位置。

代码:

class Solution {
int MOD = (int)1e9+7;
public int countPalindromicSubsequences(String s) {
char[] cs = s.toCharArray();
int n = cs.length;
int[][] f = new int[n][n];
int[] L = new int[4], R = new int[4];
Arrays.fill(L, -1);
for (int i = n - 1; i >= 0; i--) {
L[cs[i] - 'a'] = i;
Arrays.fill(R, -1);
for (int j = i; j < n; j++) {
R[cs[j] - 'a'] = j;
for (int k = 0; k < 4; k++) {
if (L[k] == -1 || R[k] == -1) continue;
int l = L[k], r = R[k];
if (l == r) f[i][j] = (f[i][j] + 1) % MOD;
else if (l == r - 1) f[i][j] = (f[i][j] + 2) % MOD;
else f[i][j] = (f[i][j] + f[l + 1][r - 1] + 2) % MOD;
}
}
}
return f[0][n - 1];
}
}
  • 时间复杂度:730. 统计不同回文子序列 : 枚举边缘字符的区间 DP 运用题_Java_46,其中730. 统计不同回文子序列 : 枚举边缘字符的区间 DP 运用题_子序列_47为字符集大小
  • 空间复杂度:730. 统计不同回文子序列 : 枚举边缘字符的区间 DP 运用题_算法_48

最后

这是我们「刷穿 LeetCode」系列文章的第 ​​No.730​​ 篇,系列开始于 2021/01/01,截止于起始日 LeetCode 上共有 1916 道题目,部分是有锁题,我们将先把所有不带锁的题目刷完。

在这个系列文章里面,除了讲解解题思路以外,还会尽可能给出最为简洁的代码。如果涉及通解还会相应的代码模板。

为了方便各位同学能够电脑上进行调试和提交代码,我建立了相关的仓库:github.com/SharingSour… 。

在仓库地址里,你可以看到系列文章的题解链接、系列文章的相应代码、LeetCode 原题链接和其他优选题解。

举报

相关推荐

0 条评论