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

148 阅读4分钟

简单记录一下《最大UCC子串》这道题解题思路。

题面如下:

问题描述

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

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

样例1:

对于字符串"UCUUCCCCC"和编辑距离限制m = 3,可以通过编辑字符串生成最多包含3个"UCC"子串的序列。例如可以通过三次添加组成"UCCUCCUCCCCC",也可以通过二次替换组成"UCCUCCUCC"

样例2:

对于字符串"U"和编辑距离限制m = 6,可以通过编辑字符串生成最多包含2个"UCC"子串的序列。例如可以通过5次插入操作组成"UCCUCC"

样例3:

对于字符串"UCCUUU"和编辑距离限制"m = 2,可以通过编辑字符串生成最多包含2个"UCC"子串的序列。例如可以通过2次替换操作组成"UCCUCC"

解题思路

下面我们先来独立思考一下这道题要怎么做:

1、首先我们应该明确我们的操作次数不超过m,操作手段可以使增,删和替换。而"UCC"子串只有三个长度,也就是说,如果我们对给定的某个序列进行各种操作结束后,还有多余3的操作次数的话,那么可以消耗3个操作次数,在不打断"UCC"子串的位置上凭空添加新的"UCC"

2、对给定字符串的处理,首先我们应该查找并标记原有字符串的"UCC"子串个数和位置,防止后期操作破坏掉该子串,在这里我们可以采用vis这一数组进行标记,并使用count记录"UCC"子串个数。

n = len(s)
vis = [False] * n
count = 0
pos = 0
    
# 查找并标记所有的 "UCC" 子串
while m >= 0 and pos + 2 < n:
    if s[pos:pos+3] == 'UCC':
        count += 1
        vis[pos], vis[pos+1], vis[pos+2] = True, True, True
        pos += 3
    else:
        pos += 1

3、其次我们寻找只需消耗1个操作次数即可组成"UCC"子串的字符子串,即字符子串应该是"UC"或者"CC"。从前向后查找未标记的"UC"子串,此时可以消耗1个操作次数将其变成一个"UCC"子串。

pos = 0
    # 查找并标记所有未标记过的 "UC" 子串
    while m > 0 and pos + 1 < n:
        if not vis[pos] and not vis[pos+1] and s[pos:pos+2] == 'UC':
            count += 1
            m -= 1
            vis[pos], vis[pos+1] = True, True
            pos += 2
        else:
            pos += 1

4、从后向前查找未标记的"CC"子串,此时可以消耗1个操作次数将其变成一个"UCC"子串。

pos = n - 1
    # 从后向前查找并标记所有未标记过的 "CC" 子串
    while m > 0 and pos - 1 >= 0:
        if not vis[pos] and not vis[pos-1] and s[pos-1:pos+1] == 'CC':
            count += 1
            m -= 1
            vis[pos], vis[pos-1] = True, True
            pos -= 2
        else:
            pos -= 1

5、寻找那些需要2次以上操作次数的字符子串,类似样例2,针对余下未标记的字符,通过消耗2次操作次数,添加两个适合的字符来组成"UCC"子串。

pos = 0
    # 标记所有剩余未标记的字符
    while m >= 2 and pos < n:
        if not vis[pos]:
            m -= 2
            count += 1
            pos += 1
        else:
            pos += 1

6、遍历完步骤5中所有未标记的字符后,若还有多余的操作次数(即编辑距离限制),消耗3次操作次数组成"UCC"子串,这也就是 步骤1 中提到的剩余操作。

最终的代码如下:

def solution(m: int, s: str) -> int:
    n = len(s)
    vis = [False] * n
    count = 0
    pos = 0
    
    # 查找并标记所有的 "UCC" 子串
    while m >= 0 and pos + 2 < n:
        if s[pos:pos+3] == 'UCC':
            count += 1
            vis[pos], vis[pos+1], vis[pos+2] = True, True, True
            pos += 3
        else:
            pos += 1
            
    pos = 0
    # 查找并标记所有未标记过的 "UC" 子串
    while m > 0 and pos + 1 < n:
        if not vis[pos] and not vis[pos+1] and s[pos:pos+2] == 'UC':
            count += 1
            m -= 1
            vis[pos], vis[pos+1] = True, True
            pos += 2
        else:
            pos += 1
            
    pos = n - 1
    # 从后向前查找并标记所有未标记过的 "CC" 子串
    while m > 0 and pos - 1 >= 0:
        if not vis[pos] and not vis[pos-1] and s[pos-1:pos+1] == 'CC':
            count += 1
            m -= 1
            vis[pos], vis[pos-1] = True, True
            pos -= 2
        else:
            pos -= 1
            
    pos = 0
    # 标记所有剩余未标记的字符
    while m >= 2 and pos < n:
        if not vis[pos]:
            m -= 2
            count += 1
            pos += 1
        else:
            pos += 1
            
    count += m // 3
    return count

可以看到测试结果是通过的。

image.png