一、代码分块解析与详细分析
1. 遍历字符串并定位目标子串
功能:
遍历字符串,找到所有等于 "chhc" 的子串起始位置。
代码:
for i in range(n - target_len + 1):
# 检查从 i 开始的长度为 target_len 的子串是否等于 "chhc"
if s[i:i + target_len] == target:
...
分析:
- 通过遍历字符串的每一个可能起点,截取长度为 4 的子串并与目标子串比较,判断是否等于
"chhc"。 - 因为目标子串长度固定为 4,循环终点为
n - target_len + 1,防止越界。
感悟:
这一部分很基础,但实现过程中容易忽略边界条件,例如当字符串长度小于目标子串时,需要特别注意处理(虽然题目隐含了目标子串存在的可能性)。
2. 计算左侧贡献
功能:
计算当前目标子串左侧的起点选择数,即以当前目标子串为右边界的子串数量。
代码:
left_contrib = i + 1
分析:
- 假设当前目标子串起始位置是
i,那么左侧可选起点是[0, 1, 2, ..., i],共计i + 1种选择。 - 这种直接利用索引计算的方法,避免了对每一种可能子串进行显式枚举,提高了效率。
感悟:
在很多涉及区间贡献的问题中, “利用索引快速计算” 是一个重要技巧。这里的贡献思想很容易推广到其他场景,比如统计某种特定模式的出现次数或求区间和等。
3. 计算右侧贡献
功能:
计算当前目标子串右侧的终点选择数,即以当前目标子串为左边界的子串数量。
代码:
right_contrib = n - (i + target_len) + 1
分析:
- 当前目标子串的终点索引为
i + target_len - 1,右侧可选的终点是[i + target_len, ..., n - 1]。 - 因此可选终点的数量为
n - (i + target_len) + 1。
感悟:
在实现这一部分时,最关键的就是明确“剩余部分”的范围及其含义。在很多复杂的字符串处理问题中,思路清晰比实现代码更重要。这里的公式实际上是滑动窗口的简化应用。
4. 累加总权值
功能:
根据当前目标子串的左侧和右侧贡献,计算其权值并累加到总权值中。
代码:
total_weight += left_contrib * right_contrib
分析:
- 当前目标子串对总权值的贡献等于其左侧贡献乘以右侧贡献,即
left_contrib × right_contrib。 - 随着遍历的进行,所有子串的权值都会被正确计算并累加。
感悟:
权值的计算公式体现了目标子串对整体的“全局影响”。这一思想在很多优化问题中非常常见,例如动态规划中的“状态转移方程”,或者在数学模型中通过组合思想求总结果。
完整代码汇总
def solution(s: str) -> int:
n = len(s) # 字符串长度
target = "chhc" # 目标子串
target_len = len(target) # 子串长度
total_weight = 0 # 权值累加器
# 遍历字符串,寻找 "chhc" 的起始位置
for i in range(n - target_len + 1):
# 检查长度为 4 的子串是否为 "chhc"
if s[i:i + target_len] == target:
# 左侧贡献:以当前子串为结尾的左侧起点选择数
left_contrib = i + 1
# 右侧贡献:以当前子串为起点的右侧终点选择数
right_contrib = n - (i + target_len) + 1
# 当前子串的权值
total_weight += left_contrib * right_contrib
return total_weight
二、测试结果分析与验证
测试样例:
-
输入:
"chhchhc"- 出现
"chhc"的位置:索引0和3。 - 索引
0贡献:1 × 3 = 3。 - 索引
3贡献:4 × 1 = 4。
总和:3 + 4 = 8。
- 出现
-
输入:
"chhcchhcchhc"- 出现
"chhc"的位置:索引0,4,8。 - 索引
0贡献:1 × 9 = 9。 - 索引
4贡献:5 × 5 = 25。 - 索引
8贡献:9 × 1 = 9。
总和:9 + 25 + 9 = 43。
- 出现
-
输入:
"hhhh"- 没有出现
"chhc",权值为0。
- 没有出现
三、个人感悟与收获
- 贡献值思想的重要性
本题的核心在于通过“左侧贡献 × 右侧贡献”快速计算出子串的权值,而不是枚举所有可能的子串。这个思想极大地提高了效率,也是一种可复用的模型。 - 索引与范围的理解
在处理字符串问题时,索引的边界和范围决定了解法的正确性。本题对每个子串的边界处理,简洁又高效。 - 效率与扩展性
本题实现了O(n)的时间复杂度,相比暴力解法的O(n^2),效率大幅提升。而且代码逻辑清晰,便于扩展到更复杂的模式匹配问题。 - 算法设计的数学美感
在很多情况下,解决问题的本质是找到“局部到全局”的计算关系。本题中从单个子串到所有子串权值的累加,体现了数学上的抽象与简洁。