leetcode
leetcode 601 ~ 650
统计不同回文子序列

统计不同回文子序列

难度:

标签:

题目描述

代码结果

运行时间: 296 ms, 内存: 16.2 MB


/*
 * Approach:
 * Using Java Streams, we can calculate the number of distinct palindromic subsequences by first identifying
 * all subsequences of the string and then filtering out the palindromic ones. We use a set to keep track of
 * distinct palindromic subsequences and return the size of the set.
 */
import java.util.HashSet;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

public class SolutionStream {
    private static final int MOD = 1_000_000_007;

    public int countPalindromicSubsequences(String s) {
        Set<String> palindromicSubsequences = IntStream.range(0, s.length())
            .boxed()
            .flatMap(i -> IntStream.range(i + 1, s.length() + 1)
                .mapToObj(j -> s.substring(i, j)))
            .filter(this::isPalindrome)
            .collect(Collectors.toSet());

        return palindromicSubsequences.size() % MOD;
    }

    private boolean isPalindrome(String str) {
        int left = 0, right = str.length() - 1;
        while (left < right) {
            if (str.charAt(left++) != str.charAt(right--)) {
                return false;
            }
        }
        return true;
    }
}

解释

方法:

这个题解使用动态规划的思想解决问题。通过维护两个数组nxt和use,nxt[i]表示以s[i]结尾的回文子序列个数,use[i]表示以s[i]结尾且s[i]已经使用过的回文子序列个数。遍历字符串s,对于每个位置j,从j-1到0遍历所有i,如果s[i]==s[j],则将nxt[i]加到答案中,并更新nxt[i]和use[i]。如果s[i]!=s[j],则只更新nxt[i]。最终返回答案对10^9+7取模的结果。

时间复杂度:

O(n^2)

空间复杂度:

O(n)

代码细节讲解

🦆
在动态规划的解决方案中,数组`nxt`和`use`的具体作用是什么?能否详细解释它们如何跟踪子序列的状态?
在这个动态规划方案中,数组`nxt`和`use`用于跟踪以每个字符`s[i]`结尾的回文子序列的状态。`nxt[i]`表示以`s[i]`结尾的回文子序列的个数。这包括所有以`s[i]`作为结尾的回文子序列,无论它们的开始位置在哪里。`use[i]`则记录了在字符`s[i]`上结尾且该字符在此之前已经在其他回文子序列中作为结尾使用过的子序列的个数。这样,`use[i]`帮助我们避免计算重复的子序列,确保每个子序列只被计算一次。
🦆
为什么在内层循环中,当`s[i] == s[j]`时,需要进行`nxt[i]`和`use[i]`的更新,这种更新方式具体是如何帮助统计回文子序列的?
当`s[i] == s[j]`时,意味着我们找到了一个新的可能的回文子序列的开始和结束点。此时,`nxt[i]`需要更新,因为我们可以通过将`s[j]`添加到以`s[i]`结尾的所有子序列来形成新的回文子序列。具体来说,`nxt[i]`的更新为`nxt[i] += x`,其中`x`是从上一个匹配的字符到当前字符`s[j]`之间的回文子序列个数。`use[i]`则记录更新前的`nxt[i]`,这样可以在后续的计算中使用,以确保不重复计算已经计算过的子序列。这种更新方式确保了能够正确地统计不同的回文子序列,尤其是涉及到重复字符的情况。
🦆
在算法中,变量`x`的角色和意义是什么?它在循环中是如何被更新和使用的?
变量`x`在算法中代表新增加的回文子序列的数量,这些子序列是由当前考虑的字符`s[j]`新增至以前的子序列中形成的。在每一轮外层循环(对于每个`s[j]`),`x`初始被设置为1,表示单个字符`s[j]`本身也是一个回文子序列。在内层循环中,每当找到一个与`s[j]`相同的字符`s[i]`时,`x`被更新为与`s[i]`有关的新增回文子序列个数,这是通过计算`nxt[i] - use[i] + 1`得到的,其中`+1`代表添加由`s[i]`和`s[j]`形成的新回文子序列。这样的更新确保了`x`始终代表从上一个匹配点到当前点新增的回文子序列数。

相关问题

最长回文子序列

给你一个字符串 s ,找出其中最长的回文子序列,并返回该序列的长度。

子序列定义为:不改变剩余字符顺序的情况下,删除某些字符或者不删除任何字符形成的一个序列。

 

示例 1:

输入:s = "bbbab"
输出:4
解释:一个可能的最长回文子序列为 "bbbb" 。

示例 2:

输入:s = "cbbd"
输出:2
解释:一个可能的最长回文子序列为 "bb" 。

 

提示:

  • 1 <= s.length <= 1000
  • s 仅由小写英文字母组成