leetcode
leetcode 1701 ~ 1750
长度为 3 的不同回文子序列

长度为 3 的不同回文子序列

难度:

标签:

题目描述

代码结果

运行时间: 107 ms, 内存: 21.2 MB


/*
 * 思路:
 * 1. 使用Java Stream API实现查找回文子序列的逻辑。
 * 2. 对于每个字符作为中心字符,使用stream来找到所有可能的回文子序列。
 * 3. 将找到的回文子序列存储在Set中以去重。
 */
import java.util.HashSet;
import java.util.Set;
import java.util.stream.IntStream;

public class PalindromicSubsequenceStream {
    public static int countPalindromicSubsequences(String s) {
        Set<String> palindromes = new HashSet<>();
        int n = s.length();
        IntStream.range(1, n - 1).forEach(i -> { // i是中心字符
            char mid = s.charAt(i);
            IntStream.range(0, i).forEach(j -> { // 遍历左边的字符
                IntStream.range(i + 1, n).forEach(k -> { // 遍历右边的字符
                    if (s.charAt(j) == s.charAt(k)) { // 左右字符相同
                        String palindrome = "" + s.charAt(j) + mid + s.charAt(k);
                        palindromes.add(palindrome);
                    }
                });
            });
        });
        return palindromes.size();
    }

    public static void main(String[] args) {
        System.out.println(countPalindromicSubsequences("aabca")); // 输出:3
        System.out.println(countPalindromicSubsequences("adc")); // 输出:0
        System.out.println(countPalindromicSubsequences("bbcbaba")); // 输出:4
    }
}

解释

方法:

本题解采用了哈希表和二分查找的结合策略。首先,使用一个哈希表idx来存储每个字符在字符串s中出现的所有索引。接着,遍历哈希表的每个键值对,将当前键作为回文子序列的中心字符。如果中心字符出现次数超过2次,则直接将结果ret加1,因为可以形成如'aaa'这样的回文子序列。然后,对于每一个非中心的字符,使用二分查找检查它是否能够在中心字符的两侧出现,从而形成如'aba'这样的回文子序列。如果可以,结果ret加1。最终,返回结果ret。

时间复杂度:

O(n)

空间复杂度:

O(n)

代码细节讲解

🦆
为什么在这个算法中,如果一个字符在字符串中出现超过两次,就可以直接计数为一个合法的回文子序列‘aaa’?
在字符串中,如果一个字符出现超过两次,至少可以选出三个相同的字符,例如字符'x'在索引i, j, k处出现(i < j < k)。可以选择这三个位置中的任意两个或三个字符来形成回文子序列(如'xx', 'xxx')。特别地,选择i, j, k这三个索引对应的字符,可以形成'xxx',这是一个长度为3的回文子序列。由于'xxx'是由同一个字符组成,因此无论如何排列,始终满足回文的条件。
🦆
在使用二分查找确定两侧字符时,为什么选择使用`bisect_left`而不是`bisect_right`?
在此算法中,使用`bisect_left`函数来找到指定元素在已排序的列表中的插入位置,使得插入后仍保持顺序。对于左侧字符的查找,`bisect_left`用于找到第一个大于或等于中心字符第一个索引的位置,确保该字符在中心字符之后。对于右侧字符的查找,`bisect_left`用于找到第一个大于或等于中心字符最后一个索引的位置,确保该字符在中心字符之前。这样可以正确地检测到两侧字符相对于中心字符的位置,从而确保构造的回文子序列有效。
🦆
在遍历非中心字符时,为什么要跳过与中心字符相同的字符,它们不可能形成有效的回文子序列吗?
在寻找长度为3的回文子序列中,如果中心字符与两侧字符相同,即形成了如'aaa'这样的子序列,这种情况已经在单独的字符出现次数超过2次的情况下计算过。在遍历非中心字符的步骤中,我们的目标是找到形如'aba'的子序列,其中a和b是不同的字符。如果两侧字符与中心字符相同,那么就重复计算了已经考虑过的情况,所以需要跳过这种情况以避免重复计数和保证算法效率。
🦆
在二分查找中,`l` 和 `r` 的比较为什么可以确定存在一个有效的‘aba’型回文子序列?具体是基于哪些位置条件?
在算法中,`l` 是通过二分查找在列表中找到第一个不小于中心字符第一个索引的字符位置,而`r` 是找到第一个不小于中心字符最后一个索引的位置。如果`l < r`成立,这意味着至少有一个字符的位置在中心字符的第一个索引之前,另外至少有一个不同的或相同的字符在中心字符的最后一个索引之后。这样的字符位置分布满足'aba'型回文子序列的结构,其中'b'是中心字符,两侧的'a'位于中心字符的两侧,从而形成一个有效的回文子序列。

相关问题