豆包MarsCode AI刷题

3 阅读5分钟

最少前缀操作问题

问题描述

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

问题分析

本题要求将字符串 S 转换为字符串 T 的一个前缀。具体操作有两种:

  1. 修改:你可以修改 S 中的某个字符,使其与 T 对应位置的字符一致。
  2. 删除:你可以删除 S 中的字符,直到 S 变成 T 的前缀。

目标是通过这些操作,最少的操作次数将 S 转换为 T 的前缀。

思路

为了找出最少的操作次数,首先需要考虑两个核心问题:

  1. 最长公共前缀:首先,最重要的是找出 S 和 T 的最长公共前缀。因为如果 S 已经有一部分与 T 相同,修改和删除操作可以从公共前缀后的部分开始,减少不必要的操作。
  2. 剩余操作:在确定了公共前缀后,我们需要考虑剩余部分的处理。剩余部分的操作主要是删除操作,即将 S 剩余部分删除掉,或者如果公共前缀长度较短,可能还需要做一些字符修改。

步骤

  1. 找到公共前缀的长度:通过比较 S 和 T 的字符,找到它们的最长公共前缀长度。最长公共前缀的字符是不需要任何操作的。
  2. 计算剩余的操作数:公共前缀之后的字符需要删除,删除的字符数是 S 剩余的字符数。实际上,我们只需要删除 S 中公共前缀之后的部分字符,剩余部分即为 S 的长度减去公共前缀的长度。

代码实现

java
public class Main {
    public static int solution(String S, String T) {
        int i = 0, j = 0;
        
        // 找到最长公共前缀
        while (i < S.length() && j < T.length() && S.charAt(i) == T.charAt(j)) {
            i++;
            j++;
        }
        
        // 计算需要修改或删除的字符数
        int operations = S.length() - i;  // 删除S中剩余的字符
        return operations;
    }

    public static void main(String[] args) {
        // 测试样例
        System.out.println(solution("aba", "abb") == 1);  // 只需要修改 'a' -> 'b'
        System.out.println(solution("abcd", "efg") == 4); // 删除整个S
        System.out.println(solution("xyz", "xy") == 1);  // 删除 'z'
        System.out.println(solution("hello", "helloworld") == 0);  // 已经是前缀,无操作
        System.out.println(solution("same", "same") == 0); // 已经相同
    }
}

java

代码解释

  1. 找到最长公共前缀

    • 我们使用了两个指针 i 和 j 分别遍历 S 和 T,并且通过比较字符来找到最长公共前缀。
    • while (i < S.length() && j < T.length() && S.charAt(i) == T.charAt(j)) 这个循环将继续,直到找到不同的字符为止。这样,i 和 j 就表示了最长公共前缀的结束位置。
  2. 删除操作

    • 一旦我们找到了最长公共前缀,就可以计算剩余的字符数,即 S.length() - i,这表示在公共前缀之后,S 需要删除的字符数。
    • 注意到我们并没有在操作中考虑修改操作,因为删除后的字符串直接与 T 的前缀对齐,这就意味着修改操作不会产生更小的操作数。
  3. 返回结果

    • 最后返回 S.length() - i,即删除操作所需要的次数。

时间复杂度

  • 时间复杂度O(min(S.length(), T.length())),因为我们只需遍历 S 和 T 的最长公共前缀部分,遍历的时间复杂度是两者长度的最小值。
  • 空间复杂度O(1),因为我们只使用了少量的变量,不需要额外的空间。

个人理解

这道题的本质是通过找到 ST 的最长公共前缀,从而减少不必要的操作。我们只需要考虑公共前缀之后的部分,剩余部分要么删除,要么修改。由于题目允许删除操作,我们直接删除剩余的部分字符是最简便的操作,因此问题的关键就在于如何高效地找到最长公共前缀。

通过逐个比较字符并更新指针 ij,我们能够在一次遍历中得到公共前缀的长度,从而计算出最少的删除操作次数。注意,题目明确指出的是将 S 变成 T 的前缀,不要求修改或删除超出公共前缀的部分,因此这个问题变得简单且高效。

扩展思考

  • 修改操作的意义:如果删除操作是被限制的,或者如果要在删除和修改之间做选择,那么我们就必须考虑修改操作的成本。在这种情况下,动态规划可能会成为一种更合适的解法。
  • 进一步优化:如果 S 和 T 非常长,虽然当前的时间复杂度是 O(min(S.length(), T.length())),但可以通过一些缓存技术来进一步优化(如滚动数组等)。然而,在本题中,直接遍历最长公共前缀已经足够高效。

总结来说,这道题考察了字符串的前缀匹配和最小化操作的问题,核心思想是通过找到最长公共前缀来减少操作次数,避免了不必要的修改操作,使得问题的解法既简洁又高效。