1+ public class Solution3399 {
2+ public int minLength (String s , int numOps ) {
3+ int n = s .length ();
4+ char [] cs = s .toCharArray ();
5+ if (check (cs , numOps , 0 ) || check (cs , numOps , 1 )) return 1 ;
6+
7+ int left = 2 ;
8+ int right = n ;
9+ while (left < right ) {
10+ int mid = left + (right - left ) / 2 ;
11+ // 边界二分 F, F,..., F, [T, T,..., T]
12+ // ----------------------^
13+ if (checkMid (cs , numOps , mid )) {
14+ right = mid ;
15+ } else {
16+ left = mid + 1 ;
17+ }
18+ }
19+ return left ;
20+ }
21+
22+ private boolean check (char [] s , int numOps , int x ) {
23+ int cnt = 0 ;
24+ for (int i = 0 ; i < s .length ; i ++) if (s [i ] % 2 != (i + x ) % 2 ) cnt ++;
25+ return cnt <= numOps ;
26+ }
27+
28+ private boolean checkMid (char [] s , int numOps , int mid ) {
29+ int needOps = 0 ;
30+ int i = 0 ;
31+ while (i < s .length ) {
32+ int st = i ;
33+ for (i ++; i < s .length && s [i ] == s [st ]; i ++) {
34+ }
35+ int span = i - st ;
36+ needOps += span / (mid + 1 );
37+ }
38+ return needOps <= numOps ;
39+ }
40+ }
41+ /*
42+ 3399. 字符相同的最短子字符串 II
43+ https://leetcode.cn/problems/smallest-substring-with-identical-characters-ii/description/
44+
45+ 第 429 场周赛 T4。
46+
47+ 给你一个长度为 n 的二进制字符串 s 和一个整数 numOps。
48+ 你可以对 s 执行以下操作,最多 numOps 次:
49+ - 选择任意下标 i(其中 0 <= i < n),并 翻转 s[i],即如果 s[i] == '1',则将 s[i] 改为 '0',反之亦然。
50+ 你需要 最小化 s 的最长 相同 子字符串 的长度,相同子字符串是指子字符串中的所有字符都相同。
51+ 返回执行所有操作后可获得的 最小 长度。
52+ 提示:
53+ 1 <= n == s.length <= 10^5
54+ s 仅由 '0' 和 '1' 组成。
55+ 0 <= numOps <= n
56+
57+ 二分 + 贪心 + 分类讨论。
58+ 但 x=1 的情况就不能使用这个调整了。考虑 s = 0110,无论是第一个 1 还是第二个 1,都位于这一段的边界,可能会影响到相邻段的答案,
59+ 因此不能直接应用这个贪心。好在 x=1 的情况下,最终的字符串只可能有两种:要么 0101...,要么 1010...。
60+ 我们把最终字符串和 s 进行比较,看是不是至多有 numOps 个字符不同即可。
61+ rating 2382 (clist.by)
62+ */
0 commit comments