Leetcode——计数二进制子串

Posted Yawn,

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Leetcode——计数二进制子串相关的知识,希望对你有一定的参考价值。

1. 题目

在这里插入图片描述

2. 题解

这里指出题目中容易引起误会和歧义的点:

  • 计算具有相同数量 0 和 1 的非空(连续)子字符串
    • 其实这里的”相同数量“说的不是 “子串” 与 “子串“ 之间的,而是”0“和”1“的:
    • ”1100“有2个0、2个1:0和1相同数量;
    • “10”有1个0、1个1:0和1相同数量;

那么我们找到0和1相间位置处,左右0和1的数量的最小值,就是能组成的子串的个数;

(1) 解法一:中心扩展(类似回文子串的判断)

  • 如果将0与1看成对应,那么符合题目要求的子串就关于其中心(中间两个元素的中间)成轴对称。
  • 如同回文字符串关于中心轴对称。不过有一点不同,就是在本问题中对称中心只能是两个字符的中间,而回文字符串的中心可以是某一个字母
  • 这个题也可以类似于统计回文字符串的数目那样,用中心扩展的方法。
  • 在循环中首先要保证,回文中心左右的两个字符
    • 如果都是1或者都是0,那么没有以这个为中心的符合要求的字串,后续就不用从中心向外扩展了
      if(leftChar == rightChar) continue
    • 如果leftChar != rightChar,就以此为中心。使用一个while循环来计数这个中心有多少个符合题意的子串。
class Solution {
    public int countBinarySubstrings(String s) {
        int result = 0;
        char[] chars = s.toCharArray();
        for(int i = 1; i < s.length(); i++){
            int left = i - 1, right = i;
            char leftChar = chars[left], rightChar = chars[right];
            if(leftChar == rightChar)
                continue;
            while(left >= 0 && right < s.length() && chars[left] == leftChar && chars[right] == rightChar){
                left--;
                right++;
                result++;
            }
        }
        return result;
    }
}

耿直解法,遇到不同值两边扩散

class Solution {
        public int countBinarySubstrings(String s) {
        int len = s.length();
        int count = 0;

        for (int i = 0; i < len - 1; i++) {
            if(s.charAt(i) != s.charAt(i+1)){
                count++;
                int j = i-1;
                int k = i+2;

                while (j >= 0 && k <= len-1){
                    if(s.charAt(j) == s.charAt(i) && s.charAt(k) == s.charAt(i+1)){
                        count++;
                        j--;
                        k++;
                    }
                    else {
                        break;
                    }
                }
            }
        }
        
        return count;
    }
}

(2) 解法二:分组计数

  • 给定的原字符串s只由0,1构成,可以将其看成多组连续的0与多组连续的1组成的。而且0与1的组是交替出现的(不交替那不就归为一组完事了)。
  • 假设有4个1与3个0相连,”1111000“ 可以构成的子串有111000,1100,10,共3个;假设有3个0与2个1相连”00011“,可以构成的子串有0011,01,共两个。可以发现我们只要知道相连的两个分组的长度,就可以直接得到这两个组可以构成几个符合题意的子串,就是两分组的中较短的长度。
  • 所以只需要通过一次遍历,记录相连两分组的长度即可。preCount表示前一个分组的长度,curCount表示当前分组的长度。
    • 遍历过程中,如果当前元素与上一个元素相等chars[i - 1] == chars[i](注意这里索引,所以循环从i=1开始),当前分组的长度+1。
    • 反之,当前分组结束,可以更新result += Math.min(preCount, curCount);。并且重置preCount = curCount; curCount = 1。
    • 这里为什么curCount重置为1呢?因为当前元素已经是下一个分组的第一个元素了,下一次循环是这个分组的第二个元素,然后才会让curCount++为2。
class Solution {
    public int countBinarySubstrings(String s) {
        int result = 0;
        char[] chars = s.toCharArray();
        int preCount = 0, curCount = 1;
        for(int i = 1; i < chars.length; i++){
            if(chars[i - 1] == chars[i]) // 记录当前组的数量
                curCount++;
            else {
                result += Math.min(preCount, curCount); // 将连续的0,1分组后,相邻两组能满足题意的子串数量即为 Math.min(preCount, curCount)
                preCount = curCount;
                curCount = 1;
            }
        }
        return result + Math.min(preCount, curCount); // 循环结束后还有最后一组要和前一组进行计数
    }
}

(3) 解法三:动态规划

没太看懂!

class Solution {
    public int countBinarySubstrings(String s) {
        //判空
        if (s == null || s.length() == 0) return 0;
        int result = 0;
        int[] dp = new int[s.length()];
        //记录上一个字符,用于循环时候的比较
        char lastChar = s.charAt(0);
        //动态规划都一样的套路,初始值手动设置
        dp[0] = 1;

        //例如10011
        //dp[] ={1,1,2,1,2}
        //理解这个数组,就理解了这个循环了
        for (int i = 1; i < s.length(); i++) {
            //如果当前选择的字符和记录到的上一个字符一样,就直接把之前的长度加1,就得到当前的字符的连续个数了
            if (s.charAt(i) == lastChar) {
                dp[i] = dp[i - 1] + 1;
            } else {
                //不一样,就记录为1.并且修改记录的字符
                dp[i] = 1;
                lastChar = s.charAt(i);
            }
            //长度为i时候,使用上最后一个字符时,是否能组成一个连续的01组合
            //例子: 01(1),括号中的1 dp[2]  = 2,然后往回找到第一个 0 ,可以不用循环,一步找到的方法是:dp[i - dp[i]]
            //dp[0] = 1,显然不能后面括号中的那个(1)不能构成连续子串
            if (i - dp[i] >= 0 && (dp[i - dp[i]] >= dp[i])) {
                result++;
            }
        }
        return result;
    }
}

以上是关于Leetcode——计数二进制子串的主要内容,如果未能解决你的问题,请参考以下文章

LeetCode 696. 计数二进制子串 [Count Binary Substrings (Easy)]

算法千题案例每日LeetCode打卡——89.计数二进制子串

算法千题案例每日LeetCode打卡——89.计数二进制子串

计数二进制子串

696. 计数二进制子串

[JavaScript 刷题] 哈希表 - 检查一个字符串是否包含所有长度为 K 的二进制子串, leetcode 1461