字符串子串权值求和解析 | 豆包MarsCode AI刷题

98 阅读5分钟

字符串子串权值求和

题目解析

image.png 通过这个问题,我们要找到一种高效的方法来计算给定字符串中所有包含目标子串的权值之和,并保证算法的时间复杂度尽量低。

解题思路

在解决这个问题时,我们可以从简单到复杂,逐步深入地理解问题并找到高效的解法。

首先,最直接的想法是生成字符串的所有子串并逐一查找 "chhc",但是这种方法效率非常低下。当字符串的长度增加时,子串的数量呈指数级增长,因此暴力法的时间复杂度会非常高,达到 (O(n^3)) 或更高。显然,这种方法在面对较长的字符串时完全不可行。

那么,有没有更高效的办法呢?答案是肯定的我们可以使用滑动窗口的思想来优化计算过程,,从而有效地解决这个问题。

思路逐步解析
  1. 滑动窗口扫描匹配目标子串
  • 我们可以使用一个滑动窗口在字符串中遍历,找到每一个 "chhc" 出现的位置。
  • 滑动窗口的长度固定为目标子串的长度,即 4。通过不断移动窗口,我们可以在字符串中高效地找到所有匹配 "chhc" 的位置。
  1. 计算权值贡献
  • 当我们找到一个 "chhc" 的位置时,需要计算它对整体权值的贡献。假设 "chhc" 出现的位置起始于索引 i,那么所有包含该 "chhc" 的子串的起点可以是从 0i,而终点可以从 i + 3 到字符串的末尾。

  • 这样,通过累积每个 "chhc" 子串的贡献,我们可以最终计算出整个字符串的权值之和。

  1. 复杂度分析

    由于滑动窗口的操作只需要对字符串进行一次完整的遍历,因此时间复杂度为 (O(n)),而不是直接生成所有子串的暴力法的 (O(n^3)) 或更高。

示例说明

让我们通过一个具体的例子来深入理解这个过程。以输入字符串 "chhchhc" 为例,我们可以逐步理解整个过程:

image.png

  1. 初始扫描
  • 字符串长度为 n = 7
  • 我们从索引 0 开始,用滑动窗口查找长度为 4 的子串。
  1. 匹配到的子串
  • 第一次匹配发生在索引 3,找到子串 "chhc"。
  • 从索引 3 到字符串末尾的每一个子串都包含 "chhc"。
  • 计算权值贡献:左侧有 3 + 1 个可能的起始点,右侧有 n - (i + 3) 个可能的终点。

通过这种方式,我们可以将每次匹配的结果积累起来,最终得到所有子串的权值之和。

代码实现详解

以下是实现代码的详细解释:

#include <iostream>
#include <string>

using namespace std;

int solution(string s) {
    int n = s.size();
    string target = "chhc";
    int targetLength = target.size();
    int totalWeight = 0;

    for (int i = 0; i <= n - targetLength; i++) {
        // 检查从当前位置开始的子串是否是 "chhc"
        if (s.substr(i, targetLength) == target) {
            // 左边的起点数:从 0 到 i
            int leftChoices = i + 1;
            // 右边的终点数:从 i + targetLength 到 n - 1
            int rightChoices = n - (i + targetLength) + 1;
            // 当前 "chhc" 的贡献
            totalWeight += leftChoices * rightChoices;
        }
    }
    
    return totalWeight;
}
代码详解
  1. 输入和变量初始化
  • 输入字符串 s,初始化变量 n 为字符串长度。
  • 目标子串为 "chhc",长度为 4,变量 totalWeight 用于存储最终的权值。
  1. 滑动窗口遍历
  • 使用 for 循环遍历字符串,从索引 0 开始,直到 n - targetLength,以确保窗口不会越界。
  • 在每个位置检查长度为 4 的子串是否等于 "chhc"。
  1. 计算贡献
  • 对于每一个匹配的 "chhc",计算其对所有包含它的子串的贡献。
  • 左边的可选起点数为 i + 1,右边的可选终点数为 n - (i + targetLength) + 1
  • 两者相乘得到该匹配的总贡献,将其累加到 totalWeight
  1. 输出结果

最后在 main 函数中调用 solution 函数,并输出结果,验证代码的正确性。

时间复杂度和优化

  • 时间复杂度

代码中我们仅对字符串进行了单次扫描,且每次匹配检查和贡献计算的复杂度都是 (O(1)),因此总时间复杂度为 (O(n)),其中 n 是字符串的长度。

这比直接生成所有子串的暴力方法要高效得多,暴力方法的时间复杂度为 (O(n^3)) 或更高。

  • 空间复杂度

代码中只用了常数级别的额外空间,因此空间复杂度为 (O(1))。

特殊情况考虑

当然,我们需要处理各种边界和特殊情况,确保代码的稳健性。

  1. 没有 "chhc" 出现

    如果输入字符串中没有 "chhc",例如 "hhhh",代码会正确返回 0

  2. 多个连续 "chhc"

    对于输入字符串包含多个 "chhc",例如 "chhcchhcchhc",代码能正确处理重叠情况并计算每个匹配的贡献。

  3. 边界值处理

    当字符串长度小于 4 时,循环条件 i <= n - targetLength 会保证不会越界访问。

总结

本题的关键在于如何高效地计算所有包含目标子串的权值之和。通过滑动窗口结合累积贡献的方法,我们可以在 (O(n)) 时间内完成计算,并处理各种特殊情况。