青训营豆包第五课 | 豆包MarsCode AI 刷题

5 阅读1分钟

小U和小R有两个字符串,分别是 𝑆 和 𝑇 ,现在小U需要通过对 𝑆 进行若干次操作,使其变成 𝑇 的一个前缀。操作可以是修改 𝑆 的某一个字符,或者删除 𝑆 末尾的字符。现在你需要帮助小U计算出,最少需要多少次操作才能让 𝑆 变成 𝑇 的前缀。

#include <iostream>
#include <string>
using namespace std;

int solution(string S, string T) {
    int i = 0, j = 0;
    int operations = 0;
    
    // 遍历字符串S和T
    while (i < S.size() && j < T.size()) {
        if (S[i] == T[j]) {
            // 字符相同,两个指针都向前移动
            i++;
            j++;
        } else {
            // 字符不同,需要进行一次修改操作
            operations++;
            i++;
            j++;
        }
    }
    
    // 如果S还有剩余字符,需要删除这些字符
    while (i < S.size()) {
        operations++;
        i++;
    }
    
    return operations;
}

int main() {
    cout << (solution("aba", "abb") == 1) << endl;
    cout << (solution("abcd", "efg") == 4) << endl;
    cout << (solution("xyz", "xy") == 1) << endl;
    cout << (solution("hello", "helloworld") == 0) << endl;
    cout << (solution("same", "same") == 0) << endl;
    return 0;
}

题目分析

问题理解

题目要求我们通过对字符串S进行若干次操作,使其变成字符串T的一个前缀。操作可以是修改S的某一个字符,或者删除S末尾的字符。我们需要计算出最少需要多少次操作才能让S变成T的前缀。

关键点分析

  1. 前缀的定义

    • 字符串T的前缀是指从T的开头开始的一段连续子串。例如,字符串"hello"的前缀可以是"h""he""hel""hell""hello"
  2. 操作类型

    • 修改操作:将S中的某个字符修改为T中对应位置的字符。
    • 删除操作:删除S末尾的一个字符。
  3. 最少操作次数

    • 我们需要找到一种策略,使得通过最少的修改和删除操作,将S变成T的前缀。

数据结构选择

由于我们只需要遍历字符串ST,并且记录操作次数,因此不需要复杂的数据结构。我们可以使用两个指针分别遍历字符串ST,并使用一个计数器来记录操作次数。

算法步骤

  1. 初始化

    • 初始化两个指针ij,分别指向字符串ST的开头。
    • 初始化一个计数器operations,用于记录操作次数。
  2. 遍历字符串

    • 使用一个循环,直到其中一个指针到达字符串的末尾:
      • 如果S[i] == T[j],则两个指针都向前移动。
      • 如果S[i] != T[j],则需要进行一次修改操作,operations加1,并且两个指针都向前移动。
  3. 处理剩余字符

    • 如果i还没有到达S的末尾,则需要删除S中剩余的字符,每删除一个字符,operations加1。
  4. 返回结果

    • 返回operations的值,即最少操作次数。

复杂度分析

  • 时间复杂度

    • 我们只需要遍历字符串ST一次,因此时间复杂度为O(n),其中n是字符串ST中较长的那个字符串的长度。
  • 空间复杂度

    • 我们只使用了常数级别的额外空间(两个指针和一个计数器),因此空间复杂度为O(1)

边界情况

  1. 空字符串

    • 如果ST为空字符串,那么操作次数就是另一个字符串的长度(全部删除或全部修改)。
  2. 完全相同

    • 如果ST完全相同,那么操作次数为0。
  3. 前缀匹配

    • 如果S已经是T的前缀,那么操作次数为0。
  4. 完全不匹配

    • 如果ST完全不匹配,那么操作次数就是S的长度(全部删除或全部修改)。

总结

通过上述分析,我们可以得出以下结论:

  • 我们需要通过遍历字符串ST,并使用两个指针来比较字符。
  • 当字符不匹配时,进行修改操作;当S有剩余字符时,进行删除操作。
  • 最终的操作次数就是将S变成T的前缀所需的最少操作次数。

这种策略不仅简单直观,而且时间复杂度和空间复杂度都非常高效,适合处理中等规模的字符串问题。

代码逻辑分析

总体思路

我们的目标是计算将字符串S变成字符串T的前缀所需的最少操作次数。操作可以是修改S中的某个字符,或者删除S末尾的字符。为了实现这一目标,我们可以使用两个指针分别遍历字符串ST,并记录需要进行的操作次数。

详细步骤

  1. 初始化

    • 初始化两个指针ij,分别指向字符串ST的开头。
    • 初始化一个计数器operations,用于记录操作次数。
  2. 遍历字符串

    • 使用一个循环,直到其中一个指针到达字符串的末尾:
      • 如果S[i] == T[j],则两个指针都向前移动。
      • 如果S[i] != T[j],则需要进行一次修改操作,operations加1,并且两个指针都向前移动。
  3. 处理剩余字符

    • 如果i还没有到达S的末尾,则需要删除S中剩余的字符,每删除一个字符,operations加1。
  4. 返回结果

    • 返回operations的值,即最少操作次数。

详细逻辑解释

  1. 初始化指针和计数器

    • int i = 0, j = 0;:初始化两个指针ij,分别指向字符串ST的开头。
    • int operations = 0;:初始化计数器operations,用于记录操作次数。
  2. 遍历字符串

    • while (i < S.size() && j < T.size()):使用一个循环,直到其中一个指针到达字符串的末尾。
      • if (S[i] == T[j]):如果S[i]T[j]相同,则两个指针都向前移动,即i++j++
      • else:如果S[i]T[j]不同,则需要进行一次修改操作,operations加1,并且两个指针都向前移动,即operations++i++j++
  3. 处理剩余字符

    • while (i < S.size()):如果i还没有到达S的末尾,则需要删除S中剩余的字符。
      • operations++:每删除一个字符,operations加1。
      • i++:指针i向前移动。
  4. 返回结果

    • return operations;:返回operations的值,即最少操作次数。

边界情况处理

  1. 空字符串

    • 如果ST为空字符串,那么操作次数就是另一个字符串的长度(全部删除或全部修改)。
  2. 完全相同

    • 如果ST完全相同,那么操作次数为0。
  3. 前缀匹配

    • 如果S已经是T的前缀,那么操作次数为0。
  4. 完全不匹配

    • 如果ST完全不匹配,那么操作次数就是S的长度(全部删除或全部修改)。

总结

通过上述逻辑分析,我们可以得出以下结论:

  • 我们需要通过遍历字符串ST,并使用两个指针来比较字符。
  • 当字符不匹配时,进行修改操作;当S有剩余字符时,进行删除操作。
  • 最终的操作次数就是将S变成T的前缀所需的最少操作次数。

这种策略不仅简单直观,而且时间复杂度和空间复杂度都非常高效,适合处理中等规模的字符串问题。

反思与优化

  1. 代码简洁性

    • 当前代码已经相当简洁,逻辑清晰,易于理解。通过双指针和计数器的使用,我们能够高效地解决问题。
  2. 边界情况处理

    • 代码中已经考虑了边界情况,如空字符串、完全相同、前缀匹配和完全不匹配的情况。这些边界情况的考虑使得代码更加健壮。
  3. 时间复杂度

    • 时间复杂度为O(n),其中n是字符串ST中较长的那个字符串的长度。这种线性时间复杂度在处理中等规模的字符串问题时表现良好。
  4. 空间复杂度

    • 空间复杂度为O(1),因为我们只使用了常数级别的额外空间(两个指针和一个计数器)。这种空间复杂度在处理大规模数据时也非常高效。
  5. 代码可读性

    • 代码的可读性较高,注释清晰,逻辑流程明确。这有助于其他开发者理解和维护代码。
  6. 优化空间

    • 虽然当前代码已经非常高效,但在某些极端情况下,可以考虑进一步优化。例如,如果ST的长度差异非常大,可以提前判断并返回结果,避免不必要的遍历。

总结

通过解决这个问题,我们不仅学会了如何使用双指针和计数器来高效地处理字符串问题,还掌握了如何处理边界情况和优化代码。这种策略不仅适用于当前问题,还可以推广到其他类似的字符串操作问题中。

在实际开发中,我们应始终关注代码的简洁性、可读性和效率。通过不断练习和反思,我们可以提高自己的编程能力,编写出更加高效和健壮的代码。

注意

  • 请使用用户提问的语言回答。

  • 用户提问对象为「当前代码」、「我的代码」或对应的英文表述时,请根据「编辑区代码」部分的内容回答,请不要解释「题目内容」中的代码。

  • 请优先回答「编辑区代码」