LeetCode#696: Count Binary Substrings

Description

Give a string s, count the number of non-empty (contiguous) substrings that have the same number of 0’s and 1’s, and all the 0’s and all the 1’s in these substrings are grouped consecutively.

Substrings that occur multiple times are counted the number of times they occur.

Example

Input: "00110011"
Output: 6
Explanation: There are 6 substrings that have equal number of consecutive 1's and 0's: "0011", "01", "1100", "10", "0011", and "01".

Notice that some of these substrings repeat and are counted the number of times they occur.

Also, "00110011" is not a valid substring because all the 0's (and 1's) are not grouped together.
Input: "10101"
Output: 4
Explanation: There are 4 substrings: "10", "01", "10", "01" that have equal number of consecutive 1's and 0's.

Note

  • s.length will be between 1 and 50,000.
  • s will only consist of “0” or “1” characters.

Solution

最直观的解法就是一个数字一个数字的尝试:

public class Solution {
    public int countBinarySubstrings(String s) {
    	int res = 0;
    	char[] chars = s.toCharArray();
        for(int i = 0; i < chars.length - 1; i++) {
        	char c = chars[i];
        	int index = i+1;
        	int cnt = 1;
        	while(index < chars.length && chars[index] == c) {
        		cnt++;
        		index++;
        	}
        	while(index < chars.length && chars[index] != c && cnt > 0) {
        		cnt--;
        		index++;
        	}
        	if(cnt == 0) {
        		res++;
        	}
        }
        return res;
    }
}

但很明显这会导致超时,我们还有很大优化的空间。对于111000这一字符串,原先的解法是先得到111000然后得到1100最后得到10,下标每次只向前移动一次,却要重复向后进行多次判断。然而,我们可以直接根据字符串中相连的10的总数知道解为3;而对于1110011000这种情况,则根据连续10总数的较小值得出解为2。

根据以上分析,我们可以先用一个数组存储连续10出现的总数。例如,对于1110011,数组内容为{3, 2, 2}。然后我们比较这个数组中当前元素和上一个元素的值,较小值即为这两个元素组成的子字符串的解。如此反复,直到遍历完整个数组得到最终解:

public class Solution3 {
    public int countBinarySubstrings(String s) {
    	int[] groups = new int[s.length()];
    	char[] chars = s.toCharArray();
    	groups[0] = 1;
    	int index = 0;
    	int res = 0;
    	for(int i = 1; i < chars.length; i++) {
    		if(chars[i] == chars[i-1]) {
    			groups[index]++;
    		} else {
    			groups[++index]++;
    		}
    	}
    	for(int i = 1; i < groups.length; i++) {
    		res += Math.min(groups[i], groups[i-1]);
    	}
    	return res;
    }
}

这一种解法的时间复杂度为O(n),而因为不知道0和1在数组中的分布情况,因此我们总是需要创建一个长度为输入字符串长度的整型数组,空间复杂度为O(n)。

还存在以下这一时间复杂度为O(n)且不需要额外空间的解法:

public class Solution2 {
	public int countBinarySubstrings(String s) {
	    int preLen = 0, currLen = 1, res = 0;
	    for (int i = 1; i < s.length(); i++) {
	        if (s.charAt(i) == s.charAt(i-1)) currLen++;
	        else {
	        	preLen = currLen;
	        	currLen = 1;
	        }
	        if (preLen >= currLen) res++;
	    }
	    return res;
	}
}

这一解法十分简洁,只用遍历输入字符串一次。例如对于1100011,当判断到110时,因为1出现的次数大于0出现的次数,则将res加一;而遍历到1100时,1出现的次数等于0出现的次数,也将res加一。因此只要满足if (preLen >= currLen)这一条件,我们就将res加一,这样,对于11000这一子字符串,我们就能得出解为2。如此反复,我们便能得到最终解。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值