刷题记录之“最少前缀操作问题”题目解析|豆包Mars Code AI刷题

173 阅读4分钟

问题描述

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

public class Main {
    public static int solution(String S, String T) {
        int minOperations = 0;
        int lenS = S.length();
        int lenT = T.length();

        // 1. 如果 S 的长度大于 T 的长度,删除 S 的末尾字符
        if (lenS > lenT) {
            minOperations += lenS - lenT;
            S = S.substring(0, lenT); // 删除多余的字符
        }

        // 2. 逐字符比较 S 和 T
        for (int i = 0; i < S.length(); i++) {
            if (S.charAt(i) != T.charAt(i)) {
                // 3. 如果字符不匹配,需要修改字符
                minOperations++;
            }
        }

        return minOperations;
    }

    public static void main(String[] args) {
        System.out.println(solution("aba", "abb") == 1);
        System.out.println(solution("abcd", "efg") == 4);
        System.out.println(solution("xyz", "xy") == 1);
        System.out.println(solution("hello", "helloworld") == 0);
        System.out.println(solution("same", "same") == 0);
    }
}

解题思路

  1. 比较字符串长度:如果 S 的长度大于 T 的长度,那么我们需要删除 S 的末尾字符,直到 S 的长度等于 T 的长度。

  2. 逐字符比较:从字符串的开头开始,逐字符比较 S 和 T,直到遇到第一个不匹配的字符。

  3. 计算操作次数

    • 如果 S 和 T 在某个位置的字符不匹配,那么我们需要修改 S 的这个字符。
    • 如果 S 的长度大于 T 的长度,那么我们需要删除 S 的末尾字符。

关键步骤解释

  1. 删除多余字符:如果 S 的长度大于 T,我们需要删除 S 的末尾字符,直到 S 的长度等于 T
  2. 逐字符比较:从字符串的开头开始,逐字符比较 S 和 T,直到遇到第一个不匹配的字符。
  3. 计算操作次数:每次遇到不匹配的字符,操作次数加一。

前缀和的技巧与应用

前缀和是一种高效处理数组相关问题的技巧,尤其适用于求取子数组的和、频率统计等场景。通过将原数组转化为前缀和数组,我们可以显著提高某些计算的效率。以下是对前缀和的基本概念、技巧和应用实例的深入探讨。

一、前缀和的基本概念

前缀和定义为数组中每个元素到该元素为止的所有元素之和。给定一个数组 nums,其前缀和数组 prefixSum 的定义如下:

  • ( prefixSum[i] = nums[0] + nums[1] + ... + nums[i] )

构建前缀和数组

构建前缀和数组的时间复杂度为 O(n),可以用以下代码实现:

vector<int> computePrefixSum(vector<int>& nums) {
    int n = nums.size();
    vector<int> prefixSum(n + 1, 0);
    for (int i = 1; i <= n; i++) {
        prefixSum[i] = prefixSum[i - 1] + nums[i - 1];
    }
    return prefixSum;
}

二、前缀和的应用技巧

  1. 快速求取子数组和: 使用前缀和可以在 O(1) 的时间内计算某个子数组的和。对于数组 nums 中的任意子数组 nums[l...r],其和可以表示为:

    [ sum(l, r) = prefixSum[r + 1] - prefixSum[l] ]

    例如:

    int rangeSum(vector<int>& prefixSum, int l, int r) {
        return prefixSum[r + 1] - prefixSum[l];
    }
    
  2. 处理复杂查询: 在某些查询问题中,前缀和数组可以与其他数据结构一起使用,例如差分数组,能简化问题并提高效率。

  3. 解决区间问题: 在有些题目中,可通过前缀和判断数组中某个区间内某种条件是否成立,比如子数组的最大和大于某个值等。

  4. 优化暴力法: 对于某些要求频繁计算子数组和的问题,暴力法的时间复杂度是 O(n^3),而应用前缀和可以将复杂度降低到 O(n^2)。

三、实际应用示例

示例:最大平均子数组

假设给定数组 nums,找出其中平均值最大且长度为 K 的子数组。利用前缀和进行求解可以简化过程。

double findMaxAverage(vector<int>& nums, int k) {
    int n = nums.size();
    vector<int> prefixSum = computePrefixSum(nums);
    double maxAvg = INT_MIN;
    
    for (int i = 0; i <= n - k; i++) {
        double avg = (prefixSum[i + k] - prefixSum[i]) / static_cast<double>(k);
        maxAvg = max(maxAvg, avg);
    }
    
    return maxAvg;
}

四、总结

前缀和是一种强大的技巧,能有效地优化多种数组相关问题的计算。通过快速构建前缀和数组并利用其性质,我们可以将原本复杂的 O(n^2) 甚至 O(n^3) 的操作降低到 O(n)。在算法解题中,灵活应用前缀和能够提高代码的效率,简化问题的复杂度,充分发挥计算机的优势。

通过这些深入的了解与应用,我们可以在面对数组和子数组等问题时,迅速而有效地找到解决方案。前缀和不仅仅是一种技巧,更是处理数据时的一种思维方式。