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

199 阅读4分钟

题目传送门

最大UCC子串计算 - MarsCode

题意简述

给定一个由字符 'U' 和 'C' 组成的字符串 S,以及一个编辑距离限制 m。编辑距离是指将字符串 S 转化为其他字符串时所需的最少编辑操作次数。允许的编辑操作包括插入、删除或替换单个字符。目标是在编辑距离不超过 m 的条件下,修改字符串 S 以包含尽可能多的 "UCC" 子串。例如,对于字符串 "UCUUCCCCC" 和编辑距离限制 m = 3,可以通过编辑字符串生成最多包含 3 个 "UCC" 子串的序列。

解题思路

  • 初步匹配:首先在字符串中寻找已经存在的 "UCC" 子串,记录这些子串的位置并标记为已访问。这一步的目的是在不进行任何编辑操作的情况下,尽可能多地找到现有的 "UCC" 子串。

  • 编辑操作策略:

  • 替换操作:在未被标记的位置,尝试通过替换操作将 "UC" 或 "CC" 转换为 "UCC"。替换操作的代价较低(1次操作),因此优先考虑。

  • 插入操作:在未被标记的位置,尝试通过插入操作形成新的 "UCC" 子串。插入操作需要 2 次操作,因此在替换操作无法满足条件时考虑。

  • 删除操作:在某些情况下,删除多余的字符可能有助于形成更多的 "UCC" 子串,但通常不如替换和插入操作有效。

  • 贪心策略:采用贪心算法,优先进行代价较低的编辑操作(替换),然后考虑插入操作,最后是删除操作。通过这种策略,尽可能多地形成 "UCC" 子串。

4. 处理剩余编辑距离:如果在上述操作后编辑距离还有剩余,计算可以通过插入操作形成的额外 "UCC" 子串数量。每 3 次插入操作可以形成一个新的 "UCC" 子串。

代码实现

import java.util.Arrays;

public class Main {
    public static int solution(int m, String s) {
        int n = s.length();
        boolean[] vis = new boolean[n];
        Arrays.fill(vis, false);
        int ret = 0;
        int pos = 0;
        
        // 初步匹配 "UCC" 子串
        while (m >= 0 && pos + 2 < n) {
            if (s.charAt(pos) == 'U' && s.charAt(pos + 1) == 'C' && s.charAt(pos + 2) == 'C') {
                ret++;
                vis[pos] = vis[pos + 1] = vis[pos + 2] = true;
                pos += 3;
            } else {
                pos++;
            }
        }
        
        // 尝试通过替换操作形成 "UCC"
        pos = 0;
        while (m > 0 && pos + 1 < n) {
            if (vis[pos] || vis[pos + 1]) {
                pos++;
                continue;
            }
            if (s.charAt(pos) == 'U' && s.charAt(pos + 1) == 'C') {
                ret++;
                m--;
                vis[pos] = vis[pos + 1] = true;
                pos += 2;
            } else {
                pos++;
            }
        }
        
        // 尝试通过替换操作形成 "CC"
        pos = n - 1;
        while (m > 0 && pos - 1 >= 0) {
            if (vis[pos] || vis[pos - 1]) {
                pos--;
                continue;
            }
            if (s.charAt(pos) == 'C' && s.charAt(pos - 1) == 'C') {
                ret++;
                m--;
                vis[pos] = vis[pos - 1] = true;
                pos -= 2;
            } else {
                pos--;
            }
        }
        
        // 处理剩余的编辑距离
        pos = 0;
        while (m >= 2 && pos < n) {
            if (vis[pos]) {
                pos++;
            } else {
                m -= 2;
                ret++;
                pos++;
            }
        }
        
        // 计算剩余编辑距离可以形成的 "UCC" 子串
        ret += m / 3;
        return ret;
    }

    public static void main(String[] args) {
        System.out.println(solution(3, "UCUUCCCCC") == 3);
        System.out.println(solution(6, "U") == 2);
        System.out.println(solution(2, "UCCUUU") == 2);
    }
}

时间复杂度分析

该算法的时间复杂度为 𝑂(𝑛),其中 𝑛 是字符串的长度。主要的时间消耗在于遍历字符串以寻找和修改子串。由于每个字符最多被访问一次,因此整体复杂度是线性的。

用到的算法和数据结构

  • 贪心算法:通过优先进行代价较低的编辑操作(替换)来最大化 "UCC" 子串的数量。贪心算法在每一步选择中都做出局部最优的选择,以期望达到全局最优。

  • 布尔数组:用于标记已经访问和处理过的字符位置,避免重复操作。布尔数组的使用使得我们可以快速判断某个位置是否已经被处理过,从而提高算法效率。

详细分析

在初步匹配阶段,我们通过遍历字符串,直接找到所有已经存在的 "UCC" 子串,并将这些位置标记为已访问。这一步不需要消耗编辑距离,因为我们只是识别现有的子串。在编辑操作阶段,我们首先尝试通过替换操作来形成新的 "UCC" 子串。替换操作的代价是 1,因此在编辑距离允许的情况下,优先进行替换操作。接下来,我们考虑插入操作,插入操作的代价是 2,因此在替换操作无法满足条件时,我们尝试通过插入操作来形成新的子串。最后,我们处理剩余的编辑距离。如果在上述操作后编辑距离还有剩余,我们计算可以通过插入操作形成的额外 "UCC" 子串数量。每 3 次插入操作可以形成一个新的 "UCC" 子串,因此我们将剩余的编辑距离除以 3,得到可以形成的额外子串数量。通过这种贪心策略,我们能够在给定的编辑距离限制下,最大化字符串中 "UCC" 子串的数量。