Kimi LeetCode 3666. 使二进制字符串全为 1 的最少操作次数 JavaScript实现
以下是 LeetCode 3666 使二进制字符串全为 1 的最少操作次数 的 JavaScript 实现,采用 BFS + 并查集(DSU) 优化,时间复杂度 O(n \cdot \alpha(n)),可通过 n \leq 10^5 的数据。
---
解题思路
1. 状态定义
用当前字符串中 `'0'` 的数量 `cur` 作为状态。目标状态是 `0`(全为 `'1'`)。
2. 状态转移
每次操作翻转恰好 `k` 个位置,设其中 `x` 个是 `'0'`(变为 `'1'`),`k-x` 个是 `'1'`(变为 `'0'`)。
翻转后 `'0'` 的数量为:
\text{cur}' = \text{cur} + (k-x) - x = \text{cur} + k - 2x
`x` 的约束:
- 最多翻转 `cur` 个 `'0'`:x \leq \min(\text{cur}, k)
- 最多翻转 `n-cur` 个 `'1'`:k-x \leq n-\text{cur} \Rightarrow x \geq \max(0, k-n+\text{cur})
因此新状态的范围是 [l, r]:
- l = \text{cur} + k - 2 \cdot \min(\text{cur}, k)
- r = \text{cur} + k - 2 \cdot \max(0, k-n+\text{cur})
3. 关键观察:奇偶性
由于 \text{cur}' = \text{cur} + k - 2x,而 2x 是偶数,所以:
\text{cur}' \equiv \text{cur} + k \pmod{2}
同一 BFS 层的所有状态,能到达的下一层状态具有相同的奇偶性。因此只需维护两个集合(偶数 / 奇数),分别存储未访问的状态。
4. 并查集优化
需要高效地在一个范围内找到所有未访问的状态并批量删除。用两个并查集分别维护偶数和奇数:
- 删除状态 `v` 时,将其索引指向下一个同奇偶性的未访问状态
- 查询时从 `find(l)` 开始,不断取下一个,直到超出范围 `r`
---
JavaScript 代码
```javascript
/**
* @param {string} s
* @param {number} k
* @return {number}
*/
var minOperations = function(s, k) {
const n = s.length;
// 统计 '0' 的数量
let cnt0 = 0;
for (let i = 0; i < n; i++) {
if (s[i] === '0') cnt0++;
}
if (cnt0 === 0) return 0;
// 并查集:分别维护未访问的偶数和奇数状态
// 偶数 2*i 对应索引 i;奇数 2*i+1 对应索引 i
const maxE = n >> 1; // floor(n/2)
const maxO = (n - 1) >> 1; // floor((n-1)/2)
const pe = new Array(maxE + 2);
const po = new Array(maxO + 2);
for (let i = 0; i < pe.length; i++) pe[i] = i;
for (let i = 0; i < po.length; i++) po[i] = i;
// 迭代版并查集 find(避免递归栈溢出)
function findE(x) {
if (x >= pe.length) return pe.length - 1;
let root = x;
while (pe[root] !== root) root = pe[root];
while (x !== root) {
const nxt = pe[x];
pe[x] = root;
x = nxt;
}
return root;
}
function findO(x) {
if (x >= po.length) return po.length - 1;
let root = x;
while (po[root] !== root) root = po[root];
while (x !== root) {
const nxt = po[x];
po[x] = root;
x = nxt;
}
return root;
}
// 从集合中移除初始状态
if ((cnt0 & 1) === 0) {
pe[cnt0 >> 1] = findE((cnt0 >> 1) + 1);
} else {
po[(cnt0 - 1) >> 1] = findO(((cnt0 - 1) >> 1) + 1);
}
let q = [cnt0];
let ans = 0;
while (q.length > 0) {
const nq = [];
for (const cur of q) {
if (cur === 0) return ans;
// 计算翻转后 '0' 数量的可达范围 [l, r]
const l = cur + k - 2 * Math.min(cur, k);
const r = cur + k - 2 * Math.max(k - n + cur, 0);
if (l > r) continue;
if ((l & 1) === 0) {
// 查询偶数集合:值 = 索引 * 2
let nxt = findE(l >> 1);
while (nxt <= maxE && (nxt << 1) <= r) {
nq.push(nxt << 1);
pe[nxt] = findE(nxt + 1); // 删除该状态
nxt = findE(nxt);
}
} else {
// 查询奇数集合:值 = 索引 * 2 + 1
let nxt = findO((l - 1) >> 1);
while (nxt <= maxO && ((nxt << 1) | 1) <= r) {
nq.push((nxt << 1) | 1);
po[nxt] = findO(nxt + 1); // 删除该状态
nxt = findO(nxt);
}
}
}
q = nq;
ans++;
}
return -1;
};
```
---
复杂度分析
项目 复杂度
时间 O(n \cdot \alpha(n)) — 每个状态最多被访问并删除一次,并查集操作近似 O(1)
空间 O(n) — 两个并查集数组 + BFS 队列
其中 \alpha(n) 是阿克曼函数的反函数,在实际应用中可视为常数。
更多推荐


所有评论(0)