Skip to content

Commit 86f0649

Browse files
authored
feat: add solutions for lc No.3900 (#5153)
1 parent 3c140f2 commit 86f0649

7 files changed

Lines changed: 547 additions & 8 deletions

File tree

solution/3900-3999/3900.Longest Balanced Substring After One Swap/README.md

Lines changed: 191 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -72,32 +72,219 @@ edit_url: https://github.com/doocs/leetcode/edit/main/solution/3900-3999/3900.Lo
7272

7373
<!-- solution:start -->
7474

75-
### 方法一
75+
### 方法一:前缀和 + 哈希表
76+
77+
设前缀和 $\textit{pre}$ 表示当前前缀中字符 `1` 的个数减去字符 `0` 的个数。那么对于一个子串,如果其中 `0``1` 的数量相等,则它对应的前缀和变化量为 $0$。
78+
79+
因此,如果在位置 $i$ 处的前缀和为 $x$,并且之前某个位置的前缀和也为 $x$,那么这两个位置之间的子串就是一个平衡子串,我们可以直接更新答案。
80+
81+
现在题目允许我们至多交换一次任意两个字符。一次交换只能让某个子串中 `1``0` 的数量差减少 $2$,因此除了前缀和变化量为 $0$ 的情况外,我们还需要考虑:
82+
83+
- 前缀和变化量为 $2$,说明子串中 `1``0` 多 2 个;此时如果字符串外部还存在至少一个 `0`,那么我们可以通过一次交换把它变成平衡子串。
84+
- 前缀和变化量为 $-2$,说明子串中 `0``1` 多 2 个;此时如果字符串外部还存在至少一个 `1`,同样可以通过一次交换把它变成平衡子串。
85+
86+
为此,我们先统计整个字符串中 `0``1` 的总数,分别记为 $\textit{cnt0}$ 和 $\textit{cnt1}$。接着用哈希表记录每个前缀和值出现的所有位置。
87+
88+
遍历字符串到位置 $i$ 时,设当前前缀和为 $\textit{pre}$:
89+
90+
- 用最早出现的 $\textit{pre}$ 来更新“不交换时”的最长平衡子串长度。
91+
- 如果存在前缀和 $\textit{pre} - 2$,那么可以尝试构造一个 `1``0` 多 2 个的子串。设其长度为 $L$,则其中 `0` 的个数为 $(L - 2) / 2$。只有当这个数量严格小于 $\textit{cnt0}$ 时,说明字符串外部还剩至少一个 `0` 可以交换进来。
92+
- 如果存在前缀和 $\textit{pre} + 2$,同理可以尝试构造一个 `0``1` 多 2 个的子串,此时需要保证其中 `1` 的个数严格小于 $\textit{cnt1}$。
93+
94+
由于同一个前缀和值越早出现,对应子串越长,所以我们优先使用最早出现的位置;如果它无法满足“外部仍有可交换字符”的条件,再尝试次早出现的位置。
95+
96+
时间复杂度 $O(n)$,空间复杂度 $O(n)$。其中 $n$ 是字符串 $s$ 的长度。
7697

7798
<!-- tabs:start -->
7899

79100
#### Python3
80101

81102
```python
82-
103+
class Solution:
104+
def longestBalanced(self, s: str) -> int:
105+
cnt0 = s.count("0")
106+
cnt1 = len(s) - cnt0
107+
pos = {0: [-1]}
108+
ans = pre = 0
109+
for i, c in enumerate(s):
110+
pre += 1 if c == "1" else -1
111+
pos.setdefault(pre, []).append(i)
112+
113+
ans = max(ans, i - pos[pre][0])
114+
if pre - 2 in pos:
115+
p = pos[pre - 2]
116+
if (i - p[0] - 2) // 2 < cnt0:
117+
ans = max(ans, i - p[0])
118+
elif len(p) > 1:
119+
ans = max(ans, i - p[1])
120+
121+
if pre + 2 in pos:
122+
p = pos[pre + 2]
123+
if (i - p[0] - 2) // 2 < cnt1:
124+
ans = max(ans, i - p[0])
125+
elif len(p) > 1:
126+
ans = max(ans, i - p[1])
127+
return ans
83128
```
84129

85130
#### Java
86131

87132
```java
88-
133+
class Solution {
134+
public int longestBalanced(String s) {
135+
int cnt0 = 0;
136+
for (int i = 0; i < s.length(); ++i) {
137+
if (s.charAt(i) == '0') {
138+
++cnt0;
139+
}
140+
}
141+
int cnt1 = s.length() - cnt0;
142+
Map<Integer, List<Integer>> pos = new HashMap<>();
143+
pos.put(0, new ArrayList<>(List.of(-1)));
144+
int ans = 0;
145+
int pre = 0;
146+
for (int i = 0; i < s.length(); ++i) {
147+
pre += s.charAt(i) == '1' ? 1 : -1;
148+
pos.computeIfAbsent(pre, k -> new ArrayList<>()).add(i);
149+
150+
ans = Math.max(ans, i - pos.get(pre).get(0));
151+
if (pos.containsKey(pre - 2)) {
152+
List<Integer> p = pos.get(pre - 2);
153+
if ((i - p.get(0) - 2) / 2 < cnt0) {
154+
ans = Math.max(ans, i - p.get(0));
155+
} else if (p.size() > 1) {
156+
ans = Math.max(ans, i - p.get(1));
157+
}
158+
}
159+
160+
if (pos.containsKey(pre + 2)) {
161+
List<Integer> p = pos.get(pre + 2);
162+
if ((i - p.get(0) - 2) / 2 < cnt1) {
163+
ans = Math.max(ans, i - p.get(0));
164+
} else if (p.size() > 1) {
165+
ans = Math.max(ans, i - p.get(1));
166+
}
167+
}
168+
}
169+
return ans;
170+
}
171+
}
89172
```
90173

91174
#### C++
92175

93176
```cpp
94-
177+
class Solution {
178+
public:
179+
int longestBalanced(string s) {
180+
int cnt0 = count(s.begin(), s.end(), '0');
181+
int cnt1 = s.size() - cnt0;
182+
unordered_map<int, vector<int>> pos;
183+
pos[0] = {-1};
184+
int ans = 0, pre = 0;
185+
for (int i = 0; i < s.size(); ++i) {
186+
pre += s[i] == '1' ? 1 : -1;
187+
pos[pre].push_back(i);
188+
189+
ans = max(ans, i - pos[pre][0]);
190+
if (pos.contains(pre - 2)) {
191+
auto& p = pos[pre - 2];
192+
if ((i - p[0] - 2) / 2 < cnt0) {
193+
ans = max(ans, i - p[0]);
194+
} else if (p.size() > 1) {
195+
ans = max(ans, i - p[1]);
196+
}
197+
}
198+
199+
if (pos.contains(pre + 2)) {
200+
auto& p = pos[pre + 2];
201+
if ((i - p[0] - 2) / 2 < cnt1) {
202+
ans = max(ans, i - p[0]);
203+
} else if (p.size() > 1) {
204+
ans = max(ans, i - p[1]);
205+
}
206+
}
207+
}
208+
return ans;
209+
}
210+
};
95211
```
96212

97213
#### Go
98214

99215
```go
216+
func longestBalanced(s string) int {
217+
cnt0 := strings.Count(s, "0")
218+
cnt1 := len(s) - cnt0
219+
pos := map[int][]int{0: {-1}}
220+
ans, pre := 0, 0
221+
for i, c := range s {
222+
if c == '1' {
223+
pre++
224+
} else {
225+
pre--
226+
}
227+
pos[pre] = append(pos[pre], i)
228+
229+
ans = max(ans, i-pos[pre][0])
230+
if p, ok := pos[pre-2]; ok {
231+
if (i-p[0]-2)/2 < cnt0 {
232+
ans = max(ans, i-p[0])
233+
} else if len(p) > 1 {
234+
ans = max(ans, i-p[1])
235+
}
236+
}
237+
238+
if p, ok := pos[pre+2]; ok {
239+
if (i-p[0]-2)/2 < cnt1 {
240+
ans = max(ans, i-p[0])
241+
} else if len(p) > 1 {
242+
ans = max(ans, i-p[1])
243+
}
244+
}
245+
}
246+
return ans
247+
}
248+
```
100249

250+
#### TypeScript
251+
252+
```ts
253+
function longestBalanced(s: string): number {
254+
const cnt0 = [...s].filter(c => c === '0').length;
255+
const cnt1 = s.length - cnt0;
256+
const pos = new Map<number, number[]>();
257+
pos.set(0, [-1]);
258+
let ans = 0;
259+
let pre = 0;
260+
for (let i = 0; i < s.length; ++i) {
261+
pre += s[i] === '1' ? 1 : -1;
262+
if (!pos.has(pre)) {
263+
pos.set(pre, []);
264+
}
265+
pos.get(pre)!.push(i);
266+
267+
ans = Math.max(ans, i - pos.get(pre)![0]);
268+
if (pos.has(pre - 2)) {
269+
const p = pos.get(pre - 2)!;
270+
if ((i - p[0] - 2) >> 1 < cnt0) {
271+
ans = Math.max(ans, i - p[0]);
272+
} else if (p.length > 1) {
273+
ans = Math.max(ans, i - p[1]);
274+
}
275+
}
276+
277+
if (pos.has(pre + 2)) {
278+
const p = pos.get(pre + 2)!;
279+
if ((i - p[0] - 2) >> 1 < cnt1) {
280+
ans = Math.max(ans, i - p[0]);
281+
} else if (p.length > 1) {
282+
ans = Math.max(ans, i - p[1]);
283+
}
284+
}
285+
}
286+
return ans;
287+
}
101288
```
102289

103290
<!-- tabs:end -->

0 commit comments

Comments
 (0)