以《最大UCC子串计算》为例,解析AI刷题功能亮点 | 豆包MarsCode AI刷题

110 阅读7分钟

引言

本次我将以《最大UCC子串计算》这道题为例,通过这篇文章来解析豆包MarsCode AI刷题的亮点所在;

问题描述

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

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

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

AI使用

看上去有些复杂,我们让豆包MarsCode AI帮我们解析一下题目:

image.png

image.png

image.png 提交完了发现错误

image.png

第二案例错误,我给ai提示

image.png 更新完代码发现ai陷入误区,我们给出他们误区,然后再让其写代码

image.png

image.png

image.png 完整代码如下

def solution(m: int, s: str) -> int:
    n = len(s)
    vis = [False] * n
    ret = 0
    pos = 0
    
    # 查找现有的 "UCC" 子串
    while m >= 0 and pos + 2 < n:
        if s[pos] == 'U' and s[pos + 1] == 'C' and s[pos + 2] == 'C':
            ret += 1
            vis[pos] = vis[pos + 1] = vis[pos + 2] = True
            pos += 3
        else:
            pos += 1
    
    pos = 0
    # 尝试通过插入 'C' 来形成 "UCC"
    while m > 0 and pos + 1 < n:
        if vis[pos] or vis[pos + 1]:
            pos += 1
            continue
        if s[pos] == 'U' and s[pos + 1] == 'C':
            ret += 1
            m -= 1
            vis[pos] = vis[pos + 1] = True
            pos += 2
        else:
            pos += 1
    
    pos = n - 1
    # 尝试通过插入 'U' 来形成 "UCC"
    while m > 0 and pos - 1 >= 0:
        if vis[pos] or vis[pos - 1]:
            pos -= 1
            continue
        if s[pos] == 'C' and s[pos - 1] == 'C':
            ret += 1
            m -= 1
            vis[pos] = vis[pos - 1] = True
            pos -= 2
        else:
            pos -= 1
    
    pos = 0
    # 尝试通过插入 'U' 和 'C' 来形成 "UCC"
    while m >= 2 and pos < n:
        if vis[pos]:
            pos += 1
        else:
            m -= 2
            ret += 1
            pos += 1
    
    # 剩余的编辑距离可以用来插入 "UCC"
    ret += m // 3
    return ret

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

image.png

代码解释

1. 函数定义及初始化

def solution(m: int, s: str) -> int:
    n = len(s)
    vis = [False] * n
    ret = 0
    pos = 0
  • 定义了一个名为 solution 的函数,它接受两个参数:m(整数类型,表示可进行的编辑操作次数)和 s(字符串类型,表示给定的字符串),函数返回一个整数。
  • 计算输入字符串 s 的长度并赋值给 n
  • 创建了一个长度为 n 的布尔型列表 vis,初始值全部为 False,用于标记字符串中的字符是否已经在形成 "UCC" 子串的过程中被使用过。
  • 初始化变量 ret 为 0,用于记录最终可以形成的 "UCC" 子串的数量。
  • 初始化变量 pos 为 0,用于在字符串中遍历的位置指针。

2. 查找现有的 "UCC" 子串

while m >= 0 and pos + 2 < n:
    if s[pos] == 'U' and s[pos + 1] == 'C' and s[pos + 2] == 'C':
        ret += 1
        vis[pos] = vis[pos + 1] = vis[pos + 2] = True
        pos += 3
    else:
        pos += 1
  • 通过一个 while 循环来遍历字符串 s,循环条件是编辑次数 m 大于等于 0 且当前位置指针 pos 加上 2 后仍小于字符串长度 n,这样可以保证能够检查到完整的三个字符组成 "UCC" 的情况。
  • 在循环内部,如果当前位置 pospos + 1 和 pos + 2 所对应的字符依次为 'U'、'C' 和 'C',则说明找到了一个现有的 "UCC" 子串。此时将 ret 的值加 1,表示找到了一个符合要求的子串,并且将 vis 列表中对应这三个位置的元素标记为 True,表示这三个字符已经被使用过,然后将位置指针 pos 向后移动 3 位,继续检查后面的字符串。
  • 如果当前位置不满足 "UCC" 的条件,则只将位置指针 pos 向后移动 1 位,继续检查下一个位置。

3. 尝试通过插入 'C' 来形成 "UCC"

pos = 0
while m > 0 and pos + 1 < n:
    if vis[pos] or vis[pos + 1]:
        pos += 1
        continue
    if s[pos] == 'U' and s[pos + 1] == 'C':
        ret += 1
        m -= 1
        vis[pos] = vis[pos + 1] = True
        pos += 2
    else:
        pos += 1
  • 首先将位置指针 pos 重新置为 0,准备再次遍历字符串。
  • 这个 while 循环的条件是编辑次数 m 大于 0 且当前位置指针 pos 加上 1 后小于字符串长度 n,目的是检查是否可以通过插入一个 'C' 来形成 "UCC" 子串。
  • 在循环内部,如果 vis[pos] 或 vis[pos + 1] 为 True,说明这两个位置的字符已经在之前的操作中被使用过,直接将位置指针 pos 向后移动 1 位并跳过本次循环的后续操作。
  • 如果当前位置 pos 的字符为 'U' 且下一个位置 pos + 1 的字符为 'C',则说明可以通过在这两个字符中间插入一个 'C' 来形成 "UCC" 子串。此时将 ret 的值加 1,表示找到了一个新的符合要求的子串形成方式,将编辑次数 m 减 1,表示已经使用了一次编辑操作,并且将 vis 列表中对应这两个位置的元素标记为 True,然后将位置指针 pos 向后移动 2 位。
  • 如果不满足上述插入 'C' 形成 "UCC" 的条件,则将位置指针 pos 向后移动 1 位,继续检查下一个位置。

4. 尝试通过插入 'U' 来形成 "UCC"

pos = n - 1
while m > 0 and pos - 1 >= 0:
    if vis[pos] or vis[pos - 1]:
        pos -= 1
        continue
    if s[pos] == 'C' and s[pos - 1] == 'C':
        ret += 1
        m -= 1
        vis[pos] = vis[pos - 1] = True
        pos -= 2
    else:
        pos -= 1
  • 将位置指针 pos 设置为字符串长度 n 减 1,即从字符串的末尾开始向前遍历。
  • 这个 while 循环的条件是编辑次数 m 大于 0 且当前位置指针 pos 减去 1 后大于等于 0,目的是检查是否可以通过插入一个 'U' 来形成 "UCC" 子串。
  • 在循环内部,如果 vis[pos] 或 vis[pos - 1] 为 True,说明这两个位置的字符已经在之前的操作中被使用过,直接将位置指针 pos 向前移动 1 位并跳过本次循环的后续操作。
  • 如果当前位置 pos 的字符为 'C' 且前一个位置 pos - 1 的字符为 'C',则说明可以通过在这两个字符前面插入一个 'U' 来形成 "UCC" 子串。此时将 ret 的值加 1,表示找到了一个新的符合要求的子串形成方式,将编辑次数 m 减 1,表示已经使用了一次编辑操作,并且将 vis 列表中对应这两个位置的元素标记为 True,然后将位置指针 pos 向前移动 2 位。
  • 如果不满足上述插入 'U' 来形成 "UCC" 的条件,则将位置指针 pos 向前移动 1 位,继续检查前一个位置。

5. 尝试通过插入 'U' 和 'C' 来形成 "UCC"

pos = 0
while m >= 2 and pos < n:
    if vis[pos]:
        pos += 1
    else:
        m -= 2
        ret += 1
        pos += 1
  • 将位置指针 pos 重新置为 0,准备再次遍历字符串。
  • 这个 while 循环的条件是编辑次数 m 大于等于 2 且当前位置指针 pos 小于字符串长度 n,目的是检查是否可以通过同时插入一个 'U' 和一个 'C' 来形成 "UCC" 子串。
  • 在循环内部,如果 vis[pos] 为 True,说明这个位置的字符已经在之前的操作中被使用过,直接将位置指针 pos 向后移动 1 位并跳过本次循环的后续操作。
  • 如果 vis[pos] 为 False,说明这个位置的字符还未被使用过,此时可以通过同时插入一个 'U' 和一个 'C' 来形成 "UCC" 子串。将编辑次数 m 减 2,表示使用了两次编辑操作,将 ret 的值加 1,表示找到了一个新的符合要求的子串形成方式,然后将位置指针 pos 向后移动 1 位。

6. 计算剩余编辑距离可形成的 "UCC" 子串数量

ret += m // 3
  • 最后,将剩余的编辑次数 m 除以 3 得到的商加到 ret 上。这是因为每 3 次编辑操作可以插入一个完整的 "UCC" 子串(假设还有足够的位置可以插入),通过这种方式计算出利用剩余编辑次数还能形成的 "UCC" 子串数量,并累加到最终的结果 ret 中。