最大UCC子串计算 | 豆包MarsCode AI刷题

200 阅读6分钟

最大UCC子串计算 | 豆包MarsCode AI刷题

最大UCC子串计算

问题描述

小S有一个由字符 'U' 和 'C' 组成的字符串 SS,并希望在编辑距离不超过给定值 mm 的条件下,尽可能多地在字符串中找到 "UCC" 子串。

编辑距离定义为将字符串 SS 转化为其他字符串时所需的最少编辑操作次数。允许的每次编辑操作是插入、删除或替换单个字符。你需要计算在给定的编辑距离限制 mm 下,能够包含最多 "UCC" 子串的字符串可能包含多少个这样的子串。

例如,对于字符串"UCUUCCCCC"和编辑距离限制m = 3,可以通过编辑字符串生成最多包含3个"UCC"子串的序列。


测试样例

样例1:

输入:m = 3,s = "UCUUCCCCC"
输出:3

样例2:

输入:m = 6,s = "U"
输出:2

样例3:

输入:m = 2,s = "UCCUUU"
输出:2

解释

样例1:可以将字符串修改为 "UCCUCCUCC"(2 次替换操作,不超过给定值 m = 3),包含 3 个 "UCC" 子串。

样例2:后面插入 5 个字符 "CCUCC"(5 次插入操作,不超过给定值 m = 6),可以将字符串修改为 "UCCUCC",包含 2 个 "UCC" 子串。

样例3:替换最后 2 个字符,可以将字符串修改为 "UCCUCC",包含 2 个 "UCC" 子串。

解决思路

采用贪心的思想考虑局部最优情况:

编辑距离就是执行操作的次数,你只有插入,替换或删除单个字符这三种操作

对于有限的操作次数,我们可以采用贪心的思想,如何使用最少的步数获得最多的UCC

先检查字符串中是否有0步就能得到的UCC,即现成的UCC进行统计,统计完之后从字符串中排除,没有消耗步数,不需要更新m

再检查字符串中是否有1步就能得到的UCC,即UC和CC,UC可以在末尾插入一个C,CC可以在首部插入一个U,统计UC和CC的数量,统计完之后从字符串中排除,有多少UC和CC,就消耗了多少步的m,更新剩余m的值

再检查是否有2步得到的UCC,即U,C。U可以在末尾插入CC,C可以插入U和C,统计字符串中U和C的数量,一个U或一个C要消耗2步,有多少单个字符就需要消耗两倍字符的步数,更新m的值

最后检查3步得到的UCC,即直接插入UCC,计算剩下的m整除3是多少就行了

m不一定足够经历以上所有的检查,把m尽可能地用完,就可以得到最优的结果

代码实现

直接用正则表达式直观地展示了思路

import re
def solution(m: int, s: str) -> int:
    # 统计各种情况的数量
    s, nUCC = re.subn(r"UCC", "AAA", s)
    s, nUC = re.subn(r"UC", "BB", s)
    s, nCC = re.subn(r"CC", "DD", s)
    nsg = len(s) - 3 * nUCC - 2 * nUC - 2 * nCC
    
    # 更新结果和步数
    res = nUCC
    if m > nUC + nCC:
        res += nUC + nCC
        m -= nUC + nCC
        if m // 2 < nsg:
            res += m // 2
        else:
            res += nsg
            m -= 2 * nsg
            res += m // 3
    else:
        res += m
    return res


if __name__ == '__main__':
    print(solution(m=3, s="UCUUCCCCC") == 3)
    print(solution(m=6, s="U") == 2)
    print(solution(m=2, s="UCCUUU") == 2)

针对贪心的解释

为什么只需要插入操作就能得到最大UCC字串

Prompt

我感觉只需要插入操作就能找到最多的UCC子串,而不需要修改和删除操作,能不能解释一下这是为什么

MarsCode AI

你的直觉是正确的,确实在某些情况下,只需要插入操作就能找到最多的 "UCC" 子串。让我们来详细解释一下为什么插入操作可能是最优的选择。

问题分析
  1. 插入操作的优势

    • 插入操作可以在字符串的任意位置添加字符,从而更容易形成 "UCC" 子串。
    • 例如,如果字符串中已经有一个 "U",我们可以通过插入两个 "C" 来形成一个 "UCC" 子串。
  2. 修改和删除操作的劣势

    • 修改操作需要替换现有的字符,这可能会破坏已经存在的 "UCC" 子串。
    • 删除操作会减少字符串的长度,从而减少形成 "UCC" 子串的机会。
具体例子

考虑以下字符串 "UCUUCCCCC" 和编辑距离限制 m = 3

  • 插入操作

    • 我们可以在 "U" 后面插入两个 "C",形成 "UCC"
    • 这样,我们可以在 "UCUUCCCCC" 中插入两个 "C",形成 "UCCUCCUCC",包含 3 个 "UCC" 子串。
  • 修改和删除操作

    • 如果使用修改或删除操作,可能会破坏已经存在的 "UCC" 子串,或者减少字符串的长度,从而减少形成 "UCC" 子串的机会。
结论

在某些情况下,插入操作确实是最优的选择,因为它可以在字符串的任意位置添加字符,从而更容易形成 "UCC" 子串。

复杂度分析

我实际上并不清楚正则表达式的具体实现,我直接让AI帮我进行算法的复杂度分析,我觉得AI应该对python的正则表达式比较清楚

Prompt

计算一下编辑区代码的算法的复杂度

MarsCode AI

复杂度分析
  1. 正则表达式替换

    • re.subn(r"UCC", "AAA", s):这个操作的时间复杂度取决于字符串 s 的长度 n。正则表达式的匹配和替换操作通常是线性时间复杂度,即 O(n)
    • re.subn(r"UC", "BB", s):同上,时间复杂度为 O(n)
    • re.subn(r"CC", "DD", s):同上,时间复杂度为 O(n)
  2. 字符串长度计算

    • len(s):计算字符串长度的时间复杂度为 O(1)
  3. 编辑距离分配

    • 这部分代码主要是一些简单的算术运算和条件判断,时间复杂度为 O(1)
总体复杂度
  • 正则表达式替换操作的时间复杂度为 O(n)
  • 字符串长度计算和编辑距离分配的时间复杂度为 O(1)

因此,总体时间复杂度为 O(n),其中 n 是字符串 s 的长度。

空间复杂度
  • 正则表达式替换操作会生成新的字符串,因此空间复杂度为 O(n)
  • 其他变量的空间复杂度为 O(1)

因此,总体空间复杂度为 O(n)

总结
  • 时间复杂度O(n),其中 n 是字符串 s 的长度。
  • 空间复杂度O(n)

可能的其他解法

这道题目一开始我就感觉动态规划可能是一个可以进行的方向,但是时间比较有限,没有进行更深入的探索,通过其他的方式获得了这道题的贪心思路,贪心的代码比较简单明了,实现起来很快,所以就只采用了这种方式完成。豆包AI给的代码框架也是以动态规划的形式给出。应该有其他的博客涉及到这道题动态规划的实现,我的工作暂时进行到了这里,大佬轻喷。