tags: [‘中等’,’贪心’,’字符串’,’力扣’]
题目
给你一个下标从 0 开始的字符串 text 和另一个下标从 0 开始且长度为 2 的字符串 pattern ,两者都只包含小写英文字母。
你可以在 text 中任意位置插入 一个 字符,这个插入的字符必须是 pattern[0] 或者 pattern[1] 。注意,这个字符可以插入在 text 开头或者结尾的位置。
请你返回插入一个字符后,text 中最多包含多少个等于 pattern 的 子序列 。
子序列 指的是将一个字符串删除若干个字符后(也可以不删除),剩余字符保持原本顺序得到的字符串。
示例 1:
输入:text = "abdcdbc", pattern = "ac"输出:4解释:如果我们在 text[1] 和 text[2] 之间添加 pattern[0] = 'a' ,那么我们得到 "abadcdbc" 。那么 "ac" 作为子序列出现 4 次。其他得到 4 个 "ac" 子序列的方案还有 "aabdcdbc" 和 "abdacdbc" 。但是,"abdcadbc" ,"abdccdbc" 和 "abdcdbcc" 这些字符串虽然是可行的插入方案,但是只出现了 3 次 "ac" 子序列,所以不是最优解。可以证明插入一个字符后,无法得到超过 4 个 "ac" 子序列。
示例 2:
输入:text = "aabb", pattern = "ab"输出:6解释:可以得到 6 个 "ab" 子序列的部分方案为 "aaabb" ,"aaabb" 和 "aabbb" 。
https://leetcode.cn/problems/maximize-number-of-subsequences-in-a-string/
初次分析
先将这道题拆解,划分为两个小问题:
- 将 pattern[0] 插入到 text 中,使得 text 中最多包含多少个等于 pattern 的子序列。
- 将 pattern[1] 插入到 text 中,使得 text 中最多包含多少个等于 pattern 的子序列。
两个子问题的答案取最大值即可。
接下来问题来了,怎么求一个字符串内包含指定字符串的子序列呢?
因为一旦解决这个问题,尝试不同的插入位置,即可算出题目的最终答案。
分析一下求子序列这个问题:例如abac,求ac子序列有多少个,怎么算?
首先明确,子序列是原字符串删除若干字符之后,剩下的字符按顺序组成的。
那么是不是可以这么理解,从原字符串中按照顺序挑选子序列长度的字符,看是否和预期的子序列相等?
嗯?好像是诶!
那么怎么遍历出所有的情况呢?直接看代码!
v1-暴力
package mainfunc maximumSubsequenceCount(text string, pattern string) int64 {if len(pattern) < 2 {return 0}// 尝试在text中插入不同位置的字符1tmpText := texttmpPatternInsert1 := make([]string, 0)for i := 0; i <= len(text); i++ {tmpText = text[:i] + pattern[0:1] + text[i:]tmpPatternInsert1 = append(tmpPatternInsert1, tmpText)}// 计算1的子序列中为pattern的最大数量maximumSubsequenceCount1 := int64(0)for _, v := range tmpPatternInsert1 {tmpPattern := genSubSeq(v, len(pattern), pattern)tmpMax := int64(0)for _, v := range tmpPattern {if v == pattern {tmpMax++}}if tmpMax > maximumSubsequenceCount1 {maximumSubsequenceCount1 = tmpMax}}// 尝试在text中插入不同位置的字符2tmpText = texttmpPatternInsert2 := make([]string, 0)for i := 0; i <= len(text); i++ {tmpText = text[:i] + pattern[1:] + text[i:]tmpPatternInsert2 = append(tmpPatternInsert2, tmpText)}// 计算2的子序列中为pattern的最大数量maximumSubsequenceCount2 := int64(0)for _, v := range tmpPatternInsert2 {tmpMax := int64(0)tmpPattern := genSubSeq(v, len(pattern), pattern)for _, v := range tmpPattern {if v == pattern {tmpMax++}}if tmpMax > maximumSubsequenceCount2 {maximumSubsequenceCount2 = tmpMax}}// 返回最大值if maximumSubsequenceCount1 > maximumSubsequenceCount2 {return maximumSubsequenceCount1} else {return maximumSubsequenceCount2}}// 给定字符串,算出它的所有子序列组成func genSubSeq(text string, subLen int, pattern string) []string {res := make([]string, 0)for i := 0; i < len(text); i++ {tmpChar := string(text[i])// 如果长度不可能够,直接continueif i+subLen > len(text) {continue}// 如果首字母不符合,直接continueif text[i] != pattern[0] {continue}tmpRes := fetchStr(text, i+1, subLen-1, pattern)for _, v := range tmpRes {// 长度if len(tmpChar)+len(v) != subLen {continue}res = append(res, tmpChar+v)}}return res}func fetchStr(text string, left int, resLen int, pattern string) []string {if resLen == 0 || left >= len(text) {return nil}res := make([]string, 0)for i := left; i < len(text); i++ {tmpChar := string(text[i])// 当前字母不符合指定下标的字符串,直接continueif text[i] != pattern[1] {continue}// 下一个字符的数据组tmpRes := fetchStr(text, i+1, resLen-1, pattern)if len(tmpRes) == 0 {res = append(res, tmpChar)} else {for _, v := range tmpRes {res = append(res, tmpChar+v)}}}return res}
优化思路
可惜,暴力法的计算逻辑太多了。
运行之后在部分用例上超时了,这是因为使用递归方式,直接暴力计算,造成了大量重复计算。
我们在仔细想想这个问题。
我们真的需要考虑插入的所有的情况吗?
真的需要吗?
我们是不是只需要考虑将字符串插入首、尾两种情况呢?
因为这两种情况,才可能出现最大的子序列数。
v2-特殊情况
只需要改动这个地方,即只考虑首位情况。

很遗憾,最后还是超时了。

贪心算法
再一次思考,这个问题可不可以更简单?
遍历字符串,并且同时统计两个字符出现的频数。如果遇见 pattern[1],就可以和前面出现过的 pattern[0] 组成子序列。
然后我们插入字符:
- 如果加上 pattern[0], 就加在字符串开头,与字符串中的 pattern[1] 组成新的子序列。
- 如果加上 pattern[1], 就加在字符串结尾,与字符串中的 pattern[0] 组成新的子序列。
最终新增的子字符串数量为两个字符频数的最大值,加到结果中并返回。
v3-贪心
func maximumSubsequenceCount(text string, pattern string) int64 {var res, cnt1, cnt2 int64for _, c := range text {if byte(c) == pattern[1] {res += cnt1cnt2++}if byte(c) == pattern[0] {cnt1++}}if cnt1 > cnt2 {return res + cnt1}return res + cnt2}
思考
如果 pattern 的长度是 3 呢?
是 m 呢?
