leetcode
leetcode 2301 ~ 2350
最长平衡子字符串

最长平衡子字符串

难度:

标签:

题目描述

You are given a binary string s consisting only of zeroes and ones.

A substring of s is considered balanced if all zeroes are before ones and the number of zeroes is equal to the number of ones inside the substring. Notice that the empty substring is considered a balanced substring.

Return the length of the longest balanced substring of s.

A substring is a contiguous sequence of characters within a string.

 

Example 1:

Input: s = "01000111"
Output: 6
Explanation: The longest balanced substring is "000111", which has length 6.

Example 2:

Input: s = "00111"
Output: 4
Explanation: The longest balanced substring is "0011", which has length 4. 

Example 3:

Input: s = "111"
Output: 0
Explanation: There is no balanced substring except the empty substring, so the answer is 0.

 

Constraints:

  • 1 <= s.length <= 50
  • '0' <= s[i] <= '1'

代码结果

运行时间: 30 ms, 内存: 16.1 MB


/*
思路:
1. 使用Stream和lambda表达式来优化子字符串的检查过程。
2. 通过将原字符串的所有可能子字符串生成流,并在流中检查平衡子字符串的最大长度。
*/
import java.util.stream.IntStream;

public class BalancedSubstringStream {
    public static int findLongestBalancedSubstring(String s) {
        return IntStream.range(0, s.length())
            .flatMap(i -> IntStream.range(i + 1, s.length() + 1)
                .map(j -> s.substring(i, j)))
            .filter(BalancedSubstringStream::isBalanced)
            .mapToInt(String::length)
            .max()
            .orElse(0);
    }

    private static boolean isBalanced(String substring) {
        long count0 = substring.chars().filter(c -> c == '0').count();
        long count1 = substring.chars().filter(c -> c == '1').count();
        if (count0 != count1) return false;
        boolean seenOne = false;
        for (char c : substring.toCharArray()) {
            if (c == '1') seenOne = true;
            if (c == '0' && seenOne) return false;
        }
        return true;
    }

    public static void main(String[] args) {
        String s1 = "01000111";
        String s2 = "00111";
        String s3 = "111";
        System.out.println(findLongestBalancedSubstring(s1)); // 6
        System.out.println(findLongestBalancedSubstring(s2)); // 4
        System.out.println(findLongestBalancedSubstring(s3)); // 0
    }
}

解释

方法:

这个题解的思路主要是基于追踪遇到的0和1的数量。初始设定一个标记flag为True表示当前正在统计0的数量。当遇到0且flag为True时,增加0的数量。若遇到1且已有0存在,将flag设置为False表示开始匹配1和之前的0。每成功匹配一对0和1时,减少0的计数并增加已匹配对的计数。若遇到0且flag为False,表示当前0不可能与前面的1匹配,因此重新设置flag为True并重置0的计数与匹配对的计数。通过维护一个最大值ans来记录至今发现的最长平衡子字符串长度。

时间复杂度:

O(n)

空间复杂度:

O(1)

代码细节讲解

🦆
在题解中,当遇到'1'且已有0存在时,你是如何确定此时应该开始匹配1和之前0而不是继续计数0的?
在题解的逻辑中,我们维护一个标志`flag`初始设为True,表示当前是在统计0的数量。当遇到'0'并且`flag`为True时,我们会继续增加0的统计数量。当我们遇到'1'时,如果已经统计了一些0(`num0`大于0),则意味着我们有潜在的0可以与此'1'匹配,从而形成平衡的0和1对。因此,此时将`flag`设置为False,开始匹配1与之前的0,而不是继续统计0,这是因为我们需要寻找平衡的子字符串,而单独增加更多的0会破坏已经开始形成的平衡结构。
🦆
题解中提到,如果遇到0且flag为False时,会重置0的数量并重新计数,这种情况下之前统计的0和1的匹配对会如何处理?是否可能会遗漏一些有效的匹配对?
当`flag`为False且遇到新的'0'时,这表示当前的1数量已经用完(与之前统计的0数量已匹配完成),再遇到0意味着之前形成的平衡区间已经结束。在这种情况下,我们将重新开始统计新的0,因为之前的匹配对已经计入了最长平衡子字符串的长度(通过`ans`变量更新)。此时重置`num0`和`res`并不会遗漏有效的匹配对,因为已匹配的对数已经被考虑过,而无法形成平衡的0或1(如额外的1或开始新的0序列)标志着需要重新开始一个新的可能的平衡区间。
🦆
题解中使用的方法似乎只在遇到1时更新最长平衡子字符串的长度,为什么没有在遇到0时也更新?是否有可能在某些情况下遗漏更新最大长度?
在题解逻辑中,最长平衡子字符串的长度只在找到一个新的匹配对即0和1匹配成功时更新,因为只有这时平衡子字符串的有效长度增加(每成功匹配一对,长度增加2)。遇到单独的0不会直接影响当前平衡子字符串的长度,因为没有相应的1与之匹配形成平衡对。只有在0和1成功匹配时,才能确定增加了平衡子字符串的长度,因此,没有在遇到0时更新是因为这不会增加任何已经存在的平衡子字符串长度。这种方法不会遗漏最大长度的更新,因为每次匹配对成功时都进行了检查和更新。

相关问题