Kimi LeetCode 3398. 字符相同的最短子字符串 I JavaScript实现
LeetCode 3398. 字符相同的最短子字符串 I — JavaScript 实现
思路
本题具有单调性:如果最长相同子串长度 `m` 可以实现,那么任何大于 `m` 的长度也一定可以实现。因此使用二分答案。
验证函数 `check(m)` 判断能否在 `numOps` 次翻转内,使最长连续相同子串长度不超过 `m`:
情况 策略
`m == 1` 字符串必须变成交替的 `"0101..."` 或 `"1010..."`。分别计算两种模式需要的翻转次数,取最小值。
`m > 1` 遍历字符串,统计每一段连续相同字符的长度 `k`。每隔 `m` 个字符翻转一次(即每 `m+1` 个位置翻一个),可将该段分割成长度均不超过 `m` 的块。所需翻转次数为 `⌊k / (m+1)⌋`。
二分搜索区间为 `[1, n]`,寻找最小的可行 `m`。
- 时间复杂度:`O(n log n)`
- 空间复杂度:`O(1)`
代码
```javascript
/**
* @param {string} s
* @param {number} numOps
* @return {number}
*/
var minLength = function(s, numOps) {
const n = s.length;
// 验证:能否在 numOps 次操作内,使最长连续相同子串长度不超过 m
const check = (m) => {
let cnt = 0; // 所需操作次数
if (m === 1) {
// 需要变成 "0101..." 或 "1010..."
// 先计算变成 "0101..." 需要翻转几次
for (let i = 0; i < n; i++) {
const expected = (i % 2 === 0) ? '0' : '1';
if (s[i] !== expected) {
cnt++;
}
}
// 另一种 "1010..." 的翻转次数为 n - cnt,取最小值
cnt = Math.min(cnt, n - cnt);
} else {
let k = 0; // 当前连续相同字符段的长度
for (let i = 0; i < n; i++) {
k++;
// 到达段尾(字符串末尾或下一个字符不同)
if (i === n - 1 || s[i] !== s[i + 1]) {
// 每隔 m 个字符翻一个,需要 k // (m + 1) 次操作
cnt += Math.floor(k / (m + 1));
k = 0;
}
}
}
return cnt <= numOps;
};
// 二分搜索最小可行值
let left = 1, right = n;
while (left < right) {
const mid = (left + right) >> 1;
if (check(mid)) {
right = mid; // mid 可行,尝试更小
} else {
left = mid + 1; // mid 不可行,需要更大
}
}
return left;
};
```
示例验证
- `s = "000001", numOps = 1`:二分最终找到 `m = 2`。`check(2)` 时,连续段 `"00000"` 长度 5,需要 `5 // 3 = 1` 次翻转,满足 `1 <= 1`,返回 2。
- `s = "0000", numOps = 2`:`check(1)` 时,变成 `"0101"` 需要 2 次翻转,满足 `2 <= 2`,返回 1。
- `s = "0101", numOps = 0`:`check(1)` 需要 0 次翻转,返回 1。
更多推荐

所有评论(0)