问题描述
小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)来解决这个问题。
算法步骤
-
定义状态:
- 我们可以使用一个二维数组
dp[i][j]来表示前i个字符的子串在编辑距离为j的情况下,最多能包含多少个"UCC"子串。
- 我们可以使用一个二维数组
-
状态转移:
-
对于每个字符,我们可以考虑以下几种操作:
- 插入:在当前位置插入一个字符,编辑距离增加1。
- 删除:删除当前字符,编辑距离增加1。
- 替换:替换当前字符,编辑距离增加1。
-
我们还需要考虑当前字符是否能形成新的
"UCC"子串。
-
-
初始化:
dp[0][0] = 0,表示空字符串在编辑距离为0的情况下,包含0个"UCC"子串。
-
最终结果:
- 最终结果为
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"子串。