@@ -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