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

91 阅读4分钟

问题描述

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

编辑距离定义为将字符串 𝑆S 转化为其他字符串时所需的最少编辑操作次数。允许的每次编辑操作是插入、删除或替换单个字符。你需要计算在给定的编辑距离限制 𝑚m 下,能够包含最多 "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"

数据结构选择

由于我们需要频繁地进行字符串操作和编辑距离计算,可以考虑使用动态规划(DP)来解决这个问题。

算法步骤

  1. 定义状态

    • 我们可以使用一个二维数组 dp[i][j] 来表示前 i 个字符的子串在编辑距离为 j 的情况下,最多能包含多少个 "UCC" 子串。
  2. 状态转移

    • 对于每个字符,我们可以考虑以下几种操作:

      • 插入:在当前位置插入一个字符,编辑距离增加1。
      • 删除:删除当前字符,编辑距离增加1。
      • 替换:替换当前字符,编辑距离增加1。
    • 我们还需要考虑当前字符是否能形成新的 "UCC" 子串。

  3. 初始化

    • dp[0][0] = 0,表示空字符串在编辑距离为0的情况下,包含0个 "UCC" 子串。
  4. 最终结果

    • 最终结果为 dp[n][m],其中 n 是字符串的长度。

代码实现

public class Main {
    public static int solution(int m, String s) {
        int n = s.length();
        int[][] dp = new int[n + 1][m + 1];

        // 初始化 dp 数组
        for (int i = 0; i <= n; i++) {
            for (int j = 0; j <= m; j++) {
                dp[i][j] = 0;
            }
        }

        // 状态转移
        for (int i = 1; i <= n; i++) {
            for (int j = 0; j <= m; j++) {
                // 考虑当前字符为 'U' 或 'C' 的情况
                if (s.charAt(i - 1) == 'U') {
                    // 尝试在后面插入 "CC"
                    if (j >= 2) {
                        dp[i][j] = Math.max(dp[i][j], dp[i - 1][j - 2] + 1);
                    }
                } else if (s.charAt(i - 1) == 'C') {
                    // 尝试在前面插入 'U' 和在后面插入 'C'
                    if (j >= 2) {
                        dp[i][j] = Math.max(dp[i][j], dp[i - 1][j - 2] + 1);
                    }
                    // 尝试在前面插入 "UC"
                    if (j >= 2) {
                        dp[i][j] = Math.max(dp[i][j], dp[i - 1][j - 2] + 1);
                    }
                }

                // 考虑前面两个或三个字符的情况
                if (i >= 2) {
                    if (s.charAt(i - 2) == 'U' && s.charAt(i - 1) == 'C') {
                        // 尝试在后面插入 'C'
                        if (j >= 1) {
                            dp[i][j] = Math.max(dp[i][j], dp[i - 2][j - 1] + 1);
                        }
                    }
                }
                // 考虑前面两个字符为 "CC" 的情况
                if (i >= 2) {
                    if (s.charAt(i - 2) == 'C' && s.charAt(i - 1) == 'C') {
                        // 尝试在前面插入 'U'
                        if (j >= 1) {
                            dp[i][j] = Math.max(dp[i][j], dp[i - 2][j - 1] + 1);
                        }
                    }
                }
                if (i >= 3) {
                    if (s.charAt(i - 3) == 'U' && s.charAt(i - 2) == 'C' && s.charAt(i - 1) == 'C') {
                        // 不需要再插入 'C',直接继承前面的状态
                        dp[i][j] = Math.max(dp[i][j], dp[i - 3][j] + 1);
                    }
                }

                // 考虑删除或替换操作
                dp[i][j] = Math.max(dp[i][j], dp[i - 1][j]);
            }
        }

        // 计算最终结果
        int maxCount = 0;
        for (int j = 0; j <= m; j++) {
            maxCount = Math.max(maxCount, dp[n][j] + (m - j) / 3);
        }
        return maxCount;
    }

    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);
        System.out.println(solution(10, "CCU") == 4);
        System.out.println(solution(7, "CCUUUCUCU") == 5);
    }
}

知识总结

  • 初始化:初始化 dp 数组。
  • 状态转移:根据当前字符和编辑距离,更新 dp 数组。
  • 检查子串:在每次状态转移时,检查是否能形成新的 "UCC" 子串。