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

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

1930. 长度为 3 的不同回文子序列 (Medium)

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

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

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

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

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

示例 1:

输入:s = "aabca"
输出:3
解释:长度为 3 的 3 个回文子序列分别是:
- "aba" ("aabca" 的子序列)
- "aaa" ("aabca" 的子序列)
- "aca" ("aabca" 的子序列)

示例 2:

输入:s = "adc"
输出:0
解释:"adc" 不存在长度为 3 的回文子序列。

示例 3:

输入:s = "bbcbaba"
输出:4
解释:长度为 3 的 4 个回文子序列分别是:
- "bbb" ("bbcbaba" 的子序列)
- "bcb" ("bbcbaba" 的子序列)
- "bab" ("bbcbaba" 的子序列)
- "aba" ("bbcbaba" 的子序列)

提示:

  • 3 <= s.length <= 10⁵
  • s 仅由小写英文字母组成

分析

仅需要统计每个字母在s中第一次出现和最后一次出现的位置,这样两个位置之间字母种类数即3字符回文子序列的个数。

比如对于字母 a,假设在 s 中是这样: xaxxxaxx (其中x代表任意字母),第一个和最后一个a出现的位置分别是1和5, 那么仅需要统计 s[2:5] 里不同字母的个数,就得到了以a开头结尾的3字符回文子序列的个数。

即使这两个 a 中间的字母又出现了a,这个统计策略也没有问题。

最后累加所有字母开头结尾的3字符回文子序列个数即可。

这样时间复杂是 O(nE),其中E是字符种类数,这里是26,n 是 s 的长度;空间复杂度 O(E)。

func countPalindromicSubsequence(s string) int {
	const letters = 26

	first, last := [letters]int{}, [letters]int{}
	for i := len(s) - 1; i >= 0; i-- {
		first[s[i]-'a'] = i
	}
	for i, c := range s {
		last[c-'a'] = i
	}

	countUniq := func(s string) int {
		set := 0
		for _, c := range s {
			set |= 1 << (c - 'a')
		}
		return bits.OnesCount(uint(set))
	}

	sum := 0
	for i := 0; i < letters; i++ {
		f, l := first[i], last[i]
		if f+1 < l {
			sum += countUniq(s[f+1 : l])
		}
	}
	return sum
}