Skip to content

Commit 6b4bc4e

Browse files
authored
feat: add solutions for lc No.3666 (#5043)
1 parent 6f39615 commit 6b4bc4e

8 files changed

Lines changed: 828 additions & 8 deletions

File tree

solution/3600-3699/3666.Minimum Operations to Equalize Binary String/README.md

Lines changed: 285 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -93,32 +93,313 @@ tags:
9393

9494
<!-- solution:start -->
9595

96-
### 方法一
96+
### 方法一:BFS + 有序集合
97+
98+
我们记字符串 $s$ 的长度为 $n$,记当前字符串中 '0' 的数量为 $\textit{cur}$,每一次操作,我们选择其中 $k$ 个下标进行翻转,其中有 $x$ 个下标从 '0' 翻转为 '1',有 $k-x$ 个下标从 '1' 翻转为 '0',则翻转后字符串中 '0' 的数量为 $\textit{cur} + k - 2x$。
99+
100+
而 $x$ 的取值需要满足以下条件:
101+
102+
1. 最多取 $\min(\textit{cur}, k)$ 个 '0',因为我们不能翻转超过 $\textit{cur}$ 个 '0',那么 $0 \leq x \leq \min(\textit{cur}, k)$。
103+
2. 最多取 $n - \textit{cur}$ 个 '1',因为我们不能翻转超过 $n - \textit{cur}$ 个 '1',那么 $k - x \leq n - \textit{cur}$,即 $x \geq k - n + \textit{cur}$。
104+
105+
因此 $x$ 的取值范围为 $[\max(k - n + \textit{cur}, 0), \min(\textit{cur}, k)]$,翻转后字符串中 '0' 的数量的取值范围为 $[\textit{cur} + k - 2 \cdot \min(\textit{cur}, k), \textit{cur} + k - 2 \cdot \max(k - n + \textit{cur}, 0)]$。
106+
107+
我们注意到,翻转后字符串中 '0' 的数量的奇偶性与翻转前字符串中 '0' 的数量的奇偶性相同。因此,我们可以使用两个有序集合分别存储 '0' 的数量为偶数和奇数的状态。
108+
109+
我们使用 BFS 来搜索状态转移图,初始状态为字符串中 '0' 的数量,目标状态为 0。每次从队列中取出一个状态 $\textit{cur}$,计算翻转后字符串中 '0' 的数量的取值范围 $[l, r]$,在有序集合中找到所有在 $[l, r]$ 范围内的状态,并将它们加入队列,同时从有序集合中删除它们。
110+
111+
如果我们在 BFS 过程中访问到了状态 0,则返回当前的操作次数;如果 BFS 结束后仍未访问到状态 0,则返回 -1。
112+
113+
时间复杂度 $O(n \log n)$,空间复杂度 $O(n)$。其中 $O(n)$ 是 BFS 过程中可能访问的状态数量,而 $O(\log n)$ 是在有序集合中插入和删除元素的时间复杂度。
97114

98115
<!-- tabs:start -->
99116

100117
#### Python3
101118

102119
```python
103-
120+
class Solution:
121+
def minOperations(self, s: str, k: int) -> int:
122+
n = len(s)
123+
ts = [SortedSet() for _ in range(2)]
124+
for i in range(n + 1):
125+
ts[i % 2].add(i)
126+
cnt0 = s.count('0')
127+
ts[cnt0 % 2].remove(cnt0)
128+
q = deque([cnt0])
129+
ans = 0
130+
while q:
131+
for _ in range(len(q)):
132+
cur = q.popleft()
133+
if cur == 0:
134+
return ans
135+
l = cur + k - 2 * min(cur, k)
136+
r = cur + k - 2 * max(k - n + cur, 0)
137+
t = ts[l % 2]
138+
j = t.bisect_left(l)
139+
while j < len(t) and t[j] <= r:
140+
q.append(t[j])
141+
t.remove(t[j])
142+
ans += 1
143+
return -1
104144
```
105145

106146
#### Java
107147

108148
```java
109-
149+
class Solution {
150+
public int minOperations(String s, int k) {
151+
int n = s.length();
152+
153+
TreeSet<Integer>[] ts = new TreeSet[2];
154+
Arrays.setAll(ts, i -> new TreeSet<>());
155+
156+
for (int i = 0; i <= n; i++) {
157+
ts[i % 2].add(i);
158+
}
159+
160+
int cnt0 = 0;
161+
for (char c : s.toCharArray()) {
162+
if (c == '0') {
163+
cnt0++;
164+
}
165+
}
166+
167+
ts[cnt0 % 2].remove(cnt0);
168+
169+
Deque<Integer> q = new ArrayDeque<>();
170+
q.offer(cnt0);
171+
172+
int ans = 0;
173+
while (!q.isEmpty()) {
174+
for (int size = q.size(); size > 0; --size) {
175+
int cur = q.poll();
176+
if (cur == 0) {
177+
return ans;
178+
}
179+
180+
int l = cur + k - 2 * Math.min(cur, k);
181+
int r = cur + k - 2 * Math.max(k - n + cur, 0);
182+
183+
TreeSet<Integer> t = ts[l % 2];
184+
185+
Integer next = t.ceiling(l);
186+
while (next != null && next <= r) {
187+
q.offer(next);
188+
t.remove(next);
189+
next = t.ceiling(l);
190+
}
191+
}
192+
ans++;
193+
}
194+
195+
return -1;
196+
}
197+
}
110198
```
111199

112200
#### C++
113201

114202
```cpp
115-
203+
class Solution {
204+
public:
205+
int minOperations(string s, int k) {
206+
int n = s.size();
207+
208+
set<int> ts[2];
209+
for (int i = 0; i <= n; i++) {
210+
ts[i % 2].insert(i);
211+
}
212+
213+
int cnt0 = count(s.begin(), s.end(), '0');
214+
ts[cnt0 % 2].erase(cnt0);
215+
216+
queue<int> q;
217+
q.push(cnt0);
218+
219+
int ans = 0;
220+
221+
while (!q.empty()) {
222+
for (int size = q.size(); size > 0; --size) {
223+
int cur = q.front();
224+
q.pop();
225+
if (cur == 0) {
226+
return ans;
227+
}
228+
229+
int l = cur + k - 2 * min(cur, k);
230+
int r = cur + k - 2 * max(k - n + cur, 0);
231+
232+
auto& t = ts[l % 2];
233+
auto it = t.lower_bound(l);
234+
235+
while (it != t.end() && *it <= r) {
236+
q.push(*it);
237+
it = t.erase(it);
238+
}
239+
}
240+
ans++;
241+
}
242+
243+
return -1;
244+
}
245+
};
116246
```
117247

118248
#### Go
119249

120250
```go
251+
func minOperations(s string, k int) int {
252+
n := len(s)
253+
254+
ts := [2]*redblacktree.Tree{
255+
redblacktree.NewWithIntComparator(),
256+
redblacktree.NewWithIntComparator(),
257+
}
258+
259+
for i := 0; i <= n; i++ {
260+
ts[i%2].Put(i, struct{}{})
261+
}
262+
263+
cnt0 := strings.Count(s, "0")
264+
ts[cnt0%2].Remove(cnt0)
265+
266+
q := []int{cnt0}
267+
ans := 0
268+
269+
for len(q) > 0 {
270+
nq := []int{}
271+
272+
for _, cur := range q {
273+
if cur == 0 {
274+
return ans
275+
}
276+
277+
l := cur + k - 2*min(cur, k)
278+
r := cur + k - 2*max(k-n+cur, 0)
279+
t := ts[l%2]
280+
281+
node, found := t.Ceiling(l)
282+
for found && node.Key.(int) <= r {
283+
val := node.Key.(int)
284+
nq = append(nq, val)
285+
t.Remove(val)
286+
node, found = t.Ceiling(l)
287+
}
288+
}
289+
290+
q = nq
291+
ans++
292+
}
293+
294+
return -1
295+
}
296+
```
297+
298+
#### TypeScript
299+
300+
```ts
301+
import { AvlTree } from '@datastructures-js/binary-search-tree';
302+
303+
function minOperations(s: string, k: number): number {
304+
const n: number = s.length;
305+
306+
const ts = [new AvlTree<number>(), new AvlTree<number>()];
307+
308+
for (let i = 0; i <= n; i++) {
309+
ts[i % 2].insert(i);
310+
}
311+
312+
let cnt0 = 0;
313+
for (const c of s) {
314+
if (c === '0') cnt0++;
315+
}
316+
317+
ts[cnt0 % 2].remove(cnt0);
318+
319+
let q: number[] = [cnt0];
320+
let ans = 0;
321+
322+
while (q.length > 0) {
323+
const nq: number[] = [];
324+
325+
for (const cur of q) {
326+
if (cur === 0) {
327+
return ans;
328+
}
329+
330+
const l = cur + k - 2 * Math.min(cur, k);
331+
const r = cur + k - 2 * Math.max(k - n + cur, 0);
332+
333+
const t = ts[l % 2];
334+
let node = t.upperBound(l, true);
335+
while (node && node.getValue() <= r) {
336+
const val = node.getValue();
337+
nq.push(val);
338+
t.remove(val);
339+
node = t.upperBound(l, false);
340+
}
341+
}
342+
343+
q = nq;
344+
ans++;
345+
}
346+
347+
return -1;
348+
}
349+
```
350+
351+
#### Rust
352+
353+
```rust
354+
use std::collections::{BTreeSet, VecDeque};
355+
356+
impl Solution {
357+
pub fn min_operations(s: String, k: i32) -> i32 {
358+
let n: i32 = s.len() as i32;
359+
let k: i32 = k;
360+
361+
let mut ts: [BTreeSet<i32>; 2] = [BTreeSet::new(), BTreeSet::new()];
362+
for i in 0..=n {
363+
ts[(i % 2) as usize].insert(i);
364+
}
365+
366+
let cnt0: i32 = s.bytes().filter(|&c| c == b'0').count() as i32;
367+
ts[(cnt0 % 2) as usize].remove(&cnt0);
368+
369+
let mut q: VecDeque<i32> = VecDeque::new();
370+
q.push_back(cnt0);
371+
372+
let mut ans: i32 = 0;
373+
374+
while !q.is_empty() {
375+
let size = q.len();
376+
for _ in 0..size {
377+
let cur = q.pop_front().unwrap();
378+
if cur == 0 {
379+
return ans;
380+
}
381+
382+
let l = cur + k - 2 * cur.min(k);
383+
let r = cur + k - 2 * (k - n + cur).max(0);
384+
385+
let parity = (l % 2) as usize;
386+
387+
let vals: Vec<i32> = ts[parity]
388+
.range(l..=r)
389+
.cloned()
390+
.collect();
391+
392+
for v in vals {
393+
q.push_back(v);
394+
ts[parity].remove(&v);
395+
}
396+
}
397+
ans += 1;
398+
}
121399

400+
-1
401+
}
402+
}
122403
```
123404

124405
<!-- tabs:end -->

0 commit comments

Comments
 (0)