题解-最大UCC字串计算 | 豆包MarsCode AI刷题

164 阅读4分钟

问题描述

小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" 子串。

解题思路

我们需要在一个由字符 'U' 和 'C' 组成的字符串中,通过不超过给定编辑距离 m 的操作(插入、删除或替换字符),尽可能多地找到子串 "UCC"

数据结构选择

  1. 动态规划(DP)表:我们可以使用一个二维的 DP 表 dp[i][e],其中 i 表示当前处理到字符串的第 i 个字符,e 表示当前已经使用的编辑操作次数。dp[i][e] 表示在字符串的前 i 个字符中,使用不超过 e 次编辑操作所能找到的最大 "UCC" 子串数量。
  2. 匹配信息表:为了记录在每个位置 i 开始,形成 "UCC" 子串所需的最小编辑操作次数,我们可以使用一个列表 match_info[i],其中每个元素是一个元组 (c, l),表示在位置 i 开始,形成 "UCC" 子串所需的最小编辑操作次数 c 和子串长度 l

算法步骤

  1. 初始化 DP 表:将 dp[0][0] 初始化为 0,表示在空字符串中不需要任何编辑操作就能找到 0 个 "UCC" 子串。

  2. 计算匹配信息:对于每个位置 i,计算从该位置开始形成 "UCC" 子串所需的最小编辑操作次数,并记录在 match_info[i] 中。

  3. 动态规划更新

    • 对于每个位置 i 和每个编辑操作次数 e,更新 dp[i+1][e] 和 dp[i+1][e+1],表示不进行编辑操作或进行一次编辑操作的情况。
    • 如果当前位置 i 可以形成 "UCC" 子串,并且编辑操作次数 e + c 不超过 m,则更新 dp[i+l][e+c],表示在当前位置形成 "UCC" 子串的情况。
  4. 结果提取:最终结果为 dp[n][e] 中的最大值,其中 n 是字符串的长度,e 是编辑操作次数。

代码实现

def solution(m: int, s: str) -> int:
    n = len(s)
    dp = [[-1] * (m + 1) for _ in range(n + 1)] 
    dp[0][0] = 0 

    match_info = [[] for _ in range(n)] 
    for i in range(n):
        max_len = min(n - i, 3 + m) 
        dp_match = [[float('inf')] * (max_len + 1) for _ in range(4)] 
        dp_match[0][0] = 0
        for p in range(4):  
            for q in range(max_len + 1):  
                if dp_match[p][q] > m:  
                    continue
                if p < 3 and q < max_len:  
                    cost = 0 if s[i + q] == 'UCC'[p] else 1
                    dp_match[p + 1][q + 1] = min(dp_match[p + 1][q + 1], dp_match[p][q] + cost)
                if p < 3: 
                    dp_match[p + 1][q] = min(dp_match[p + 1][q], dp_match[p][q] + 1)
                if q < max_len: 
                    dp_match[p][q + 1] = min(dp_match[p][q + 1], dp_match[p][q] + 1)

        for q in range(max_len + 1):
            c = dp_match[3][q]
            match_info[i].append((c, q))  
 
    for i in range(n + 1):
        for e in range(m + 1):
            if dp[i][e] == -1:
                continue
            if i < n:  
                dp[i + 1][e] = max(dp[i + 1][e], dp[i][e]) 
                if e + 1 <= m:  
                    dp[i + 1][e + 1] = max(dp[i + 1][e + 1], dp[i][e])            
            if i < n and match_info[i]: 
                for c, l in match_info[i]:  
                    if e + c <= m and i + l <= n:
                        dp[i + l][e + c] = max(dp[i + l][e + c], dp[i][e] + 1)
 
    max_substrings = 0
    for e in range(m + 1):
        max_substrings = max(max_substrings, dp[n][e])
    return max_substrings

总结

通过动态规划和匹配信息的结合,我们可以有效地计算出在给定编辑距离限制下,字符串中最多能包含多少个 "UCC" 子串。这个方法的时间复杂度为 O(n * m^2),其中 n 是字符串的长度,m 是编辑距离限制。