问题描述
小M给你一个长度为nn的01串,串只包含字符 '0' 和 '1'。你可以从这个串中删除一个前缀和一个后缀(可以为空),即保留原串的一个连续子串。操作后的代价包括两部分:
- 被删除的字符 '1' 的数量;
- 剩余子串中字符 '0' 的数量。
你的任务是找出如何进行操作,才能使代价最小,并输出这个最小代价。
测试样例
样例1:
输入:
s = "101110110"
输出:2
样例2:
输入:
s = "111011"
输出:1
样例3:
输入:
s = "0000"
输出:0
Python实现
思路说明
题目要求通过删除一个前缀和一个后缀,使得操作后的代价最小。代价包括两部分:被删除的字符 '1' 的数量和剩余子串中字符 '0' 的数量。我们可以通过前缀和数组来快速计算任意子串中 '0' 和 '1' 的数量,然后通过双层循环遍历所有可能的子串,计算每种情况下的代价,并取最小值。
解题过程
-
前缀和数组的构建:
- 使用两个前缀和数组
sum1
和sum2
,分别记录从字符串开始到当前位置的 '0' 和 '1' 的数量。 - 遍历字符串
s
,根据字符是 '0' 还是 '1',更新sum1
和sum2
。
- 使用两个前缀和数组
-
初始化答案:
- 初始化答案
ans
为字符串中所有 '1' 的数量,即sum2[n]
,表示不删除任何前缀和后缀的情况。
- 初始化答案
-
双层循环遍历所有子串:
- 外层循环遍历子串的起点
i
,内层循环遍历子串的终点j
。 - 对于每个子串
s[i-1:j]
,计算其代价:- 剩余子串中 '0' 的数量为
sum1[j] - sum1[i-1]
。 - 被删除的前缀中 '1' 的数量为
sum2[i-1]
。 - 被删除的后缀中 '1' 的数量为
sum2[n] - sum2[j]
。
- 剩余子串中 '0' 的数量为
- 更新答案
ans
为所有情况中的最小值。
- 外层循环遍历子串的起点
-
返回结果:
- 最终返回
ans
,即最小的代价。
- 最终返回
复杂度分析
-
时间复杂度:
- 构建前缀和数组的时间复杂度为 O(n)。
- 双层循环遍历所有子串的时间复杂度为 O(n2)。
- 因此,总的时间复杂度为 O(n2)。
-
空间复杂度:
- 使用两个前缀和数组
sum1
和sum2
,空间复杂度为 O(n)。
- 使用两个前缀和数组
知识点扩展
-
前缀和数组:
- 前缀和数组是一种常用的技巧,用于快速计算任意子数组或子串的和或数量。在本题中,我们使用前缀和数组来记录 '0' 和 '1' 的数量,从而在 O(1) 时间内计算任意子串中 '0' 和 '1' 的数量。
-
动态规划:
- 虽然本题没有显式地使用动态规划,但通过遍历所有可能的子串并计算代价,本质上是一种动态规划的思想。通过前缀和数组和双层循环,我们能够有效地解决问题。
-
双层循环优化:
- 在实际应用中,如果 n 较大,O(n2) 的时间复杂度可能会导致性能问题。可以通过进一步优化算法,例如使用滑动窗口或单调队列等技巧,将时间复杂度降低到 O(n) 或 O(nlogn)。
代码实现
def solution(s):
n = len(s)
sum1 = [0] * (n + 1) # 前缀中 '0' 的数量
sum2 = [0] * (n + 1) # 前缀中 '1' 的数量
# 计算前缀中 '0' 和 '1' 的数量
for i in range(1, n + 1):
sum1[i] = sum1[i - 1]
sum2[i] = sum2[i - 1]
if s[i - 1] == '0':
sum1[i] += 1
else:
sum2[i] += 1
ans = sum2[n] # 初始化答案为所有 '1' 的数量
# 遍历所有可能的子串
for i in range(1, n + 1):
for j in range(i, n + 1):
ans = min(ans, sum1[j] - sum1[i - 1] + sum2[i - 1] + sum2[n] - sum2[j])
return ans
# 测试用例
print(solution("101110110") == 2)
print(solution("111011") == 1)
print(solution("0000") == 0)
C++实现
问题理解
你有一个只包含字符 '0' 和 '1' 的字符串 s
。你可以删除一个前缀和一个后缀,保留一个连续的子串。操作的代价包括两部分:
- 被删除的字符 '1' 的数量。
- 剩余子串中字符 '0' 的数量。
目标是找到一种删除方式,使得总代价最小。
代码实现
#include <iostream>
#include <vector>
#include <string>
using namespace std;
int solution(string s) {
int n = s.length();
vector<int> sum1(n + 1, 0); // 前缀中 '0' 的数量
vector<int> sum2(n + 1, 0); // 前缀中 '1' 的数量
// 计算前缀中 '0' 和 '1' 的数量
for (int i = 1; i <= n; ++i) {
sum1[i] = sum1[i - 1];
sum2[i] = sum2[i - 1];
if (s[i - 1] == '0') {
sum1[i] += 1;
} else {
sum2[i] += 1;
}
}
int ans = sum2[n]; // 初始化答案为所有 '1' 的数量
// 遍历所有可能的子串
for (int i = 1; i <= n; ++i) {
for (int j = i; j <= n; ++j) {
ans = min(ans, sum1[j] - sum1[i - 1] + sum2[i - 1] + sum2[n] - sum2[j]);
}
}
return ans;
}
int main() {
// 测试用例
cout << (solution("101110110") == 2) << endl;
cout << (solution("111011") == 1) << endl;
cout << (solution("0000") == 0) << endl;
return 0;
}