Medium
题目描述
给你一个由数字 1 到 9 组成的字符串 s 和一个整数 k。
如果一个字符串 s 的分割满足以下条件,则称其为 好分割:
s的每个数字恰好属于一个子字符串。- 每个子字符串的值都小于等于
k。
返回 s 的好分割中子字符串的 最少数目。如果不存在 s 的好分割,则返回 -1。
注意:
- 字符串的值是其作为整数解释时的结果。例如,
"123"的值是123,"1"的值是1。 - 子字符串 是字符串中字符的连续序列。
示例 1:
输入:s = "165462", k = 60
输出:4
解释:我们可以将字符串分割为子字符串 "16"、"54"、"6" 和 "2"。每个子字符串的值都小于等于 k = 60。
可以证明我们无法将字符串分割为少于 4 个子字符串。
示例 2:
输入:s = "238182", k = 5
输出:-1
解释:这个字符串不存在好分割。
提示:
1 <= s.length <= 10^5s[i]是从'1'到'9'的数字1 <= k <= 10^9
解题思路
这道题可以使用贪心算法来解决。
核心思路: 为了使分割的子字符串数目最少,我们应该尽可能让每个子字符串都尽可能长,也就是说在满足值不超过 k 的前提下,尽可能多地包含字符。
算法步骤:
- 从左到右遍历字符串,维护当前子字符串的数值
- 对于每个位置,尝试将当前字符加入当前子字符串
- 如果加入后的值仍然 ≤ k,则继续扩展
- 如果加入后的值 > k,则需要开始一个新的子字符串
- 特别注意:如果单个字符的值就 > k,则无法形成好分割,返回 -1
贪心策略的正确性: 由于我们的目标是最小化分割数,每次都尽可能地延长当前子字符串是最优的选择。如果在某个位置提前分割,只会增加总的分割数而不会减少。
时间复杂度优化: 由于字符串可能很长,直接用字符串转整数可能会溢出。我们可以在计算过程中及时判断是否会超过 k,避免大数运算。
代码实现
class Solution {
public:
int minimumPartition(string s, int k) {
int n = s.length();
int count = 0;
int i = 0;
while (i < n) {
long long current = 0;
int start = i;
// 尝试构建尽可能长的子字符串
while (i < n) {
int digit = s[i] - '0';
// 检查是否会溢出或超过k
if (current > (k - digit) / 10) {
break;
}
current = current * 10 + digit;
if (current > k) {
break;
}
i++;
}
// 如果没有前进(单个字符就超过k),返回-1
if (i == start) {
return -1;
}
count++;
}
return count;
}
};
class Solution:
def minimumPartition(self, s: str, k: int) -> int:
n = len(s)
count = 0
i = 0
while i < n:
current = 0
start = i
# 尝试构建尽可能长的子字符串
while i < n:
digit = int(s[i])
# 检查是否会超过k
if current > (k - digit) // 10:
break
current = current * 10 + digit
if current > k:
break
i += 1
# 如果没有前进(单个字符就超过k),返回-1
if i == start:
return -1
count += 1
return count
public class Solution {
public int MinimumPartition(string s, int k) {
int n = s.Length;
int count = 0;
int i = 0;
while (i < n) {
long current = 0;
int start = i;
// 尝试构建尽可能长的子字符串
while (i < n) {
int digit = s[i] - '0';
// 检查是否会溢出或超过k
if (current > (k - digit) / 10) {
break;
}
current = current * 10 + digit;
if (current > k) {
break;
}
i++;
}
// 如果没有前进(单个字符就超过k),返回-1
if (i == start) {
return -1;
}
count++;
}
return count;
}
}
/**
* @param {string} s
* @param {number} k
* @return {number}
*/
var minimumPartition = function(s, k) {
let count = 0;
let i = 0;
while (i < s.length) {
let current = 0;
let j = i;
while (j < s.length) {
let newValue = current * 10 + parseInt(s[j]);
if (newValue > k) {
break;
}
current = newValue;
j++;
}
if (j === i) {
return -1;
}
count++;
i = j;
}
return count;
};
复杂度分析
| 复杂度 | 分析 |
|---|---|
| 时间复杂度 | O(n) |
| 空间复杂度 | O(1) |
说明:
- 时间复杂度: 每个字符最多被访问一次,总体为 O(n)
- 空间复杂度: 只使用了常数个变量,为 O(1)