[LeetCode] 1930. Unique Length-3 Palindromic Subsequences

Given a string s, return the number of unique palindromes of length three that are a subsequence of s.

Note that even if there are multiple ways to obtain the same subsequence, it is still only counted once.

A palindrome is a string that reads the same forwards and backwards.

A subsequence of a string is a new string generated from the original string with some characters (can be none) deleted without changing the relative order of the remaining characters.

For example, "ace" is a subsequence of "abcde".

Example 1:
Input: s = "aabca"
Output: 3
Explanation: The 3 palindromic subsequences of length 3 are:

  • "aba" (subsequence of "aabca")
  • "aaa" (subsequence of "aabca")
  • "aca" (subsequence of "aabca")

Example 2:
Input: s = "adc"
Output: 0
Explanation: There are no palindromic subsequences of length 3 in "adc".

Example 3:
Input: s = "bbcbaba"
Output: 4
Explanation: The 4 palindromic subsequences of length 3 are:

  • "bbb" (subsequence of "bbcbaba")
  • "bcb" (subsequence of "bbcbaba")
  • "bab" (subsequence of "bbcbaba")
  • "aba" (subsequence of "bbcbaba")

Constraints:
3 <= s.length <= 105
s consists of only lowercase English letters.

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

给你一个字符串 s ,返回 s 中 长度为 3 的不同回文子序列 的个数。

即便存在多种方法来构建相同的子序列,但相同的子序列只计数一次。

回文 是正着读和反着读一样的字符串。

子序列 是由原字符串删除其中部分字符(也可以不删除)且不改变剩余字符之间相对顺序形成的一个新字符串。

例如,"ace" 是 "abcde" 的一个子序列。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/unique-length-3-palindromic-subsequences
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

思路一

这道题是对回文串的考察。题目问的是有多少个 unique 的,由三个字母组成的回文串的子序列。因为题目要求找的回文串只能是由三个字母组成,为了确定到底哪些回文串是合法的,我这里需要先统计一下每个字母的出现次数。只有出现次数大于等于 2 的字母,才有可能作为回文串两边的字母。

我们需要记录 input 字符串中每个不同字母第一次和最后一次出现位置的下标,这里我用一个二维数组 int[][] indexes 记录。同时我还用另外一个一维数组 count 记录每个字母的出现次数。记录完毕之后,再次扫描 indexes 数组。对于其中的每一个字母,分如下几种情况

  • 如果当前字母出现次数 <= 1,那么他无法作为回文串两边的字母,直接就跳过了
  • 如果当前字母出现次数 > 1,他可以作为回文串两边的字母

对于第二种情况,我们需要统计在中间到底有哪些其他的不同字母。这里我们只能再写一个 for 循环然后用 hashset 去记录。最后 hashset 的大小就是以当前字母作为回文串两边的字母能形成的合法的回文串的个数了。将这个结果累加到结果集里。

复杂度

时间O(n)
空间O(n)

代码

Java实现

class Solution {
    public int countPalindromicSubsequence(String s) {
        int[] count = new int[26]; // 每个字母出现的次数
        int[][] indexes = new int[26][2]; // 每个字母第一次和最后一次出现的index
        for (int i = 0; i < s.length(); i++) {
            if (count[s.charAt(i) - 'a'] == 0) {
                indexes[s.charAt(i) - 'a'][0] = i; // 第一次出现的index
            }
            count[s.charAt(i) - 'a']++;
        }

        int[] count2 = new int[26];
        for (int i = s.length() - 1; i >= 0; i--) {
            if (count2[s.charAt(i) - 'a'] == 0) {
                indexes[s.charAt(i) - 'a'][1] = i; // 最后一次出现的index
                count2[s.charAt(i) - 'a'] = -1;
            }
        }

        int res = 0;
        HashSet<Character> set = new HashSet<>();
        for (int i = 0; i < 26; i++) {
            // 当前字母无法成为左右两侧的字母
            if (count[i] <= 1) {
                continue;
            }
            int firstIndex = indexes[i][0];
            int lastIndex = indexes[i][1];
            // System.out.println(firstIndex + ", " + lastIndex + ", " + count[i]);
            if (lastIndex - firstIndex > 1) {
                set.clear();
                for (int j = firstIndex + 1; j < lastIndex; j++) {
                    set.add(s.charAt(j));
                }
                res += set.size();
            }
        }
        return res;
    }
}

思路二

另外一种思路是我们遍历 26 个字母,找到他们在 input 字符串里最左边出现的下标 i 和最右边出现的下标 j。如果 j - i < 2,则说明中间无法放入别的字母组成回文。如果 j - i >= 2,则用一个 hashset 去统计中间到底有多少个不同的字母。假设有 x 个不同字母,那么就有 x 种回文,把这个结果累加到 res 里。

复杂度

时间O(n)
空间O(n)

代码

Java实现

class Solution {
    public int countPalindromicSubsequence(String s) {
        int n = s.length();
		int res = 0;
        Set<Character> set = new HashSet<>();
		for (char c = 'a'; c <= 'z'; c++) {
			int i = 0;
			int j = n - 1;
			while (i < n && s.charAt(i) != c) {
				i++;
			}
			while (j >= 0 && s.charAt(j) != c) {
				j--;
			}
			if (j - i < 2) {
				continue;
			}
            set.clear();
			for (int k = i + 1; k < j; k++) {
				set.add(s.charAt(k));
			}
			res += set.size();
		}
		return res;
    }
}
// 枚举两边的字母,然后看中间有几个不同的字母
posted @ 2021-07-13 01:29  CNoodle  阅读(371)  评论(0)    收藏  举报