LeetCode#696: Count Binary Substrings

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_38283262/article/details/83928365

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。如此反复,我们便能得到最终解。

猜你喜欢

转载自blog.csdn.net/qq_38283262/article/details/83928365