字符串中最多数目的子序列

标签: 贪心 字符串 前缀和

难度: Medium

给你一个下标从 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" 。

提示:

  • 1 <= text.length <= 105
  • pattern.length == 2
  • text 和 pattern 都只包含小写英文字母。

Submission

运行时间: 82 ms

内存: 16.6 MB

class Solution:
    def maximumSubsequenceCount(self, text: str, pattern: str) -> int:
        x, y = pattern[0], pattern[1]
        if x == y:
            cnt = text.count(x) + 1
            return (cnt - 1) * cnt // 2
        ret = 0
        cntx, cnty = 0, 0
        for c in text:
            if c == x:
                cntx += 1
            elif c == y:
                ret += cntx
                cnty += 1
        ret += max(cntx, cnty)
        return ret

Explain

该题解通过遍历字符串text,统计模式pattern中第一个字符x和第二个字符y的出现次数以及可以形成的子序列数量。特别注意的是,若x和y相同,即pattern由两个相同的字符组成,其处理逻辑不同:直接计算所有可能位置插入新字符后的子序列数量。主要步骤为:1. 如果x和y相同,计算插入后的字符数量,利用组合公式计算可形成的子序列数。2. 如果x和y不同,遍历text,每次遇到x,增加cntx;遇到y时,将cntx累加到结果ret中,并增加cnty。最后,考虑插入一个字符后,取cntx和cnty中的最大值加到ret中,这反映了通过插入一个字符(x或y)可以额外增加的子序列数量。

时间复杂度: O(n)

空间复杂度: O(1)

# 解决问题的类

class Solution:
    def maximumSubsequenceCount(self, text: str, pattern: str) -> int:
        x, y = pattern[0], pattern[1]  # pattern的第一和第二个字符
        if x == y:  # 如果两个字符相同
            cnt = text.count(x) + 1  # 计算x在text中的出现次数并考虑一个额外的插入
            return (cnt - 1) * cnt // 2  # 使用组合数公式计算子序列总数
        ret = 0  # 初始化结果
        cntx, cnty = 0, 0  # 初始化x和y的计数器
        for c in text:  # 遍历text中的每个字符
            if c == x:  # 如果字符是x
                cntx += 1  # 增加x的计数
            elif c == y:  # 如果字符是y
                ret += cntx  # 将x的计数累加到结果中,因为每个x都可以与之前的y形成pattern
                cnty += 1  # 增加y的计数
        ret += max(cntx, cnty)  # 插入一个x或y可能增加的最大子序列数
        return ret

Explore

在遍历过程中,每次遇到字符y时,仅将当前字符x的计数(cntx)累加到结果(ret)中。这种方法确保每个y只与其前面的所有x形成子序列,避免了重复计数。

算法通过在最终结果中添加max(cntx, cnty)来考虑插入一个字符x或y后可能增加的最大子序列数。这种方法表明,插入的最优位置不是固定的,而是可以在任何位置插入新字符,以最大化可能的子序列数量。

当pattern由两个相同字符组成时,每个字符都可以与之前出现的相同字符形成一个有效的子序列。使用组合数公式是为了计算从text中选取两个相同字符进行配对的所有可能方式。`(cnt - 1) * cnt // 2`计算的是从cnt个字符中任选两个形成子序列的组合数。

选择`max(cntx, cnty)`是为了找出通过插入一个额外的字符(x或y)可以带来的最大增益。如果插入x,可能会与所有现有的y形成新的子序列;同理,插入y时,每个新的y都可以与所有已存在的x配对。因此,选择两者中的最大值,以最大化新增子序列的数量。