问题描述
小S有一个由字符 U和 C 组成的字符串 SS,并希望在编辑距离不超过给定值 mm 的条件下,尽可能多地在字符串中找到 UCC子串。
编辑距离定义为将字符串 SS 转化为其他字符串时所需的最少编辑操作次数。允许的每次编辑操作是插入、删除或替换单个字符。你需要计算在给定的编辑距离限制 mm 下,能够包含最多 UCC 子串的字符串可能包含多少个这样的子串。
例如,对于字符串UCUUCCCCC和编辑距离限制m = 3,可以通过编辑字符串生成最多包含3个UCC子串的序列。
问题理解
小S有一个由字符 U 和 C 组成的字符串 SS,并希望在编辑距离不超过给定值 mm 的条件下,尽可能多地在字符串中找到 UCC 子串。 编辑距离定义为将字符串 SS 转化为其他字符串时所需的最少编辑操作次数。允许的每次编辑操作是插入、删除或替换单个字符。你需要计算在给定的编辑距离限制 mm 下,能够包含最多 UCC 子串的字符串可能包含多少个这样的子串。 例如,对于字符串UCUUCCCCC和编辑距离限制m = 3,可以通过编辑字符串生成最多包含3个UCC子串的序列。
代码详情
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);
}
}
知识总结
整体功能概述 这段代码旨在解决在给定编辑距离限制m的情况下,计算由字符U和C组成的字符串s经过编辑操作后最多能包含的UCC子串数量的问题。通过动态规划的方式,利用二维数组记录不同状态下的子串数量情况,最终得出符合要求的最大子串数量结果。
代码结构与关键部分分析 :
- 初始化二维数组: 获取输入字符串
s的长度n,然后创建一个二维数组dp,其维度为(n + 1)×(m + 1),通过两层循环将数组中的元素全部初始化为0,这一步为后续记录和更新不同状态下的相关数据做准备,是动态规划常见的初始化操作。 - 状态转移部分:
基于当前字符的操作分析: 通过循环遍历字符串的每个位置(索引从1到
n),针对当前字符是U或者C的情况分别进行处理。当字符为U时,如果编辑距离j大于等于2,就尝试在后面插入CC来构造UCC子串,并更新dp数组对应位置的值,取当前值和插入操作后能得到的更大值(使用max函数实现)。若当前字符是C,同样在编辑距离满足条件时,会考虑在前面插入U、在后面插入C或者在前面插入UC等不同操作情况来尝试增加UCC子串数量,同时更新dp值。 基于前面字符组合的操作考量: 会检查前面两个或三个字符的组合情况来决定进一步操作。例如当字符串中前面两个字符是UC且编辑距离j大于等于1时,尝试在后面插入C来凑成UCC并更新dp数组的值;对于前面两个字符是CC等其他组合情况也有相应插入操作的判断与dp值更新逻辑。并且当出现连续的UCC三个字符时,直接继承前面状态的值,这些都是依据字符串局部特征来探索如何有效增加子串数量的处理方式。 考虑删除或替换操作影响: 还会将当前状态与不进行插入等额外操作(即继承前一个位置相同编辑距离下的状态)进行比较,取两者中的最大值更新dp数组当前位置的值(同样通过max函数),以此来涵盖删除或替换操作可能带来的不同结果情况,始终保持记录每个状态下最优(最大子串数量)的情况。 - 计算最终结果: 通过循环遍历编辑距离维度(从0到
m),计算最终的最大子串数量maxCount。这里除了考虑dp[n][j](即字符串末尾位置不同编辑距离剩余情况下已有的最大子串数量)之外,还会根据剩余编辑距离能额外插入UCC子串的可能性(通过(m - j) // 3计算,因为理论上插入一个UCC子串最多消耗3次编辑距离)来综合确定最终的最大数量,每次都取当前maxCount和新计算值中的最大值,最终得到整个字符串在给定编辑距离内最多能包含的UCC子串数量。 - 主函数部分(对应Python中测试逻辑部分): 在代码中类似
print(solution(3, "UCUUCCCCC") == 3)这样的语句起到了对solution函数进行简单测试的作用,传入不同的编辑距离值和字符串实例,通过输出比较结果(判断返回的布尔值情况)来初步验证solution函数在各种输入场景下是否能输出预期的正确结果,方便对代码功能进行基本的检查和确认。
涉及的核心知识点
- 动态规划方法运用: 利用二维数组
dp来记录不同阶段、不同编辑距离剩余情况下的中间状态,基于前面已经计算好的状态逐步推导出后续状态,避免了重复计算,有效解决了在多种编辑操作和编辑距离限制交织下寻找最优(最大子串数量)解的复杂问题,把复杂的字符串编辑与子串数量计算过程拆解为有规律的状态转移和求值流程。 - 字符串索引及字符判断处理: 代码中通过类似按索引获取字符串字符的方式(虽然Python中语法和Java不同,但逻辑类似)来判断字符是
U还是C,进而依据不同字符以及其所处位置和周围字符组合情况进行相应的插入、删除、替换等操作可能性分析,合理运用索引来考察不同位置字符间的关联,这是对字符串进行逻辑处理的重要手段。 - 条件判断与状态更新逻辑: 大量运用条件判断语句(如
if语句),根据字符情况、编辑距离剩余量等多种条件来决定是否执行相关操作以及如何更新dp数组中的状态值,始终借助max函数来确保每个状态下记录的是最大子串数量,以此保证最终结果的最优性,体现了基于不同条件进行灵活逻辑处理和状态维护的编程思路。 总体而言,这段代码融合了动态规划、字符串处理以及逻辑判断等编程知识要点,展示了如何运用合适的算法和代码结构去解决特定的字符串编辑与子串数量计算这类实际问题。
学习建议
在写代码过程中,要先掌握代码整体框架,明确该实验计算模块的功能,循环和判断条件是为怎么分类的,便于查错。