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循环来计数这个中心有多少个符合题意的子串。
- 如果都是1或者都是0,那么没有以这个为中心的符合要求的字串,后续就不用从中心向外扩展了
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.计数二进制子串
[JavaScript 刷题] 哈希表 - 检查一个字符串是否包含所有长度为 K 的二进制子串, leetcode 1461