一道二进制子串算法,让面试官都解不出来?

一道二进制子串算法,让面试官都解不出来?_第1张图片
一道二进制子串算法,让面试官都解不出来?_第2张图片
算法题目:

给定一个字符串 s ,计算具有相同数量0和1的非空(连续)子字符串的数量,并且这些子字符串中的所有0和所有1都是组合在一起的。

重复出现的 子串要计算它们出现的次数。

示例1:

输入:“00110011”
输出:6
解释:有6个子串具有相同数量的连续1和0:

“0011”,“01”,“1100”,“10”,“0011”,“01”。

注意,一些重复出现的子串要计算它们出现的次数,另外,
“00110011”不是有效的子串,因为所有的0(和1)没有组合在一起。

示例2:

输入:“10101”
输出:4
解释:有4个子串,“10”,“01”,“10”,“01”,它们具有相同数量的连续1和0。

注意:s.length 在1到50,000之间的范围,s只包含“0”或“1”字符。

“000111”中有多少个有效的二进制子串?
“11100”中有多少个有效的二进制子串?
“00011100”呢?

这道题目,难度系数为简单题目,涉及到的知识点为字符串。

给定函数体如下:

/**
 * @param {string} s
 * @return {number}
 */
var countBinarySubstrings = function(s) {

};

题目理解:

通过看看这两个示例,字符串 s 给的都是二进制数,要求计算具有相同数量 0 和 1 的非空(连续)子字符串的数量,这句话里面的条件有三个:

第一 不为空,非空(连续)

第二 0 和 1 是要相同数量的

第三 0 和 1 要是连续出现的子字符串的数量

描述:

如果遇到10或者是01的情况,则说明连续的1或者是连续的0断了,那么可以拿到前面连续1或者是连续0的数量,然后再查找后面连续1或者是连续0的数量,作比较看看有多少个符合的子串。

多种解题思路:

我看到这个题之后,想起来好几种解法,都写下来可能篇幅较长,如果只是简单罗列可能不太好找,就做了这个目录导图,可以对照目录图去看。

一道二进制子串算法,让面试官都解不出来?_第3张图片
第一种JavaScript方法:按照前后数量判断:

"1100"中有1和0的数量相等则有两个符合的子串

“11000”,“0000111”中,1和0的数量不相等,则有:

min(1的数量,0的数量)个符合的子串。

如果遇到10或者是01的情况,则说明连续的1或者是连续的0都断了,那么就可以拿到前面连续1或者是0的数量了,然后在往后找连续的0或者是1的数量。接着看看有多少个符合的子串,之后持续向后查找。

var countBinarySubstrings = function(s) {
    // 前面一个数,和当前数,返回count计数
    let pre=0, count=0, count1=0, count2=0;
    // 循环字符串
    for(let i = 1; i < s.length; i++) {
    // 本质不等于,则计数返回往上加
    // 遇到10或01的情况,则说明连续的1或连续的0断了
    // s[1] !== s[0]
    // 则说明它断了,计数+1达成条件
    if(s[i] !== s[pre]) {
      if(count1 === 0) {
        // 拿到前面连续1或0的数量
        count1 = i - pre;
        pre = i;
      } else {
        // 不等于0的情况
        count2 = i - pre;
        count += count1 > count2 ? count2 : count1;
        count1 = count2;
        count2 = 0;
        pre = i;
      }
    }
    if(i === s.length - 1) {
      count2 = s.length - pre;
      count += count1 > count2 ? count2 : count1;
    }
  }

  return count;
}

看完代码得知,返回count,循环字符串从1,开始,如果s[1]
不等于前一个数,即可能是01,或者是10的情况下,那么前面的数量为当前1-0为1,前一个数量为1的情况,pre被赋值为i,i为1的情况。

假设情况s为:

var s = "001"

如果s[i]==s[pre]的数,s[i]=0 , s[pre]=0 .

count1=0 , count2=0 . pre=0 . count=0

第二次循环,s[i]=1 , s[pre]=0 . 两者是不相等的,所以有

s[i] !== s[pre]为true,此时count1为0,走下面的代码块。所以结果为:

count1=2 , count2=0 . pre=2 . count=0

因为最后了i === s.length - 1这种情况成立,所以输出结果为

count1=2 , count2=1 . pre=2 . count=1

一道二进制子串算法,让面试官都解不出来?_第4张图片
一道二进制子串算法,让面试官都解不出来?_第5张图片
其实可以看出代码中一开始都会走count1===0这条路线。pre为递增下标数,比较当前值与前一个值的情况。每次(pre=i)

一道二进制子串算法,让面试官都解不出来?_第6张图片
一道二进制子串算法,让面试官都解不出来?_第7张图片
看图带入,解释一下,本质,如果循环中两者不相等
(s[i] !== s[pre])走里面的情况,如果前者和后者比较为相等情况,不走if(s[i] !== s[pre])里的代码块,知道不相等的情况。最后一个数到了最后即尾部,满足条件(i === s.length - 1),执行其中的代码块。

一道二进制子串算法,让面试官都解不出来?_第8张图片

function daCount(s) {
    // 前面一个数,和当前数
    let pre=0, count=0, count1=0, count2=0;
    // 循环字符串
    for(let i = 1; i < s.length; i++) {
    // 遇到10或01的情况,则说明连续的1或连续的0断了
    console.log('s[i]='+s[i]+"  ,  "+"s[pre]="+s[pre]+ " . ");
    if(s[i] !== s[pre]) {
      console.log('第一次count1=' + count1 + " , " + "count2=" + count2 + " . " + "pre=" + pre + " . " + "count=" + count);
      if(count1 === 0) {
        // 拿到前面连续1或0的数量
        count1 = i - pre;
        pre = i;
     console.log('第二次count1=' + count1 + " , " + "count2=" + count2 + " . " + "pre=" + pre + " . " + "count=" + count);
      } else {
        count2 = i - pre;
        count += count1 > count2 ? count2 : count1;

        count1 = count2;
        count2 = 0;
        pre = i;
console.log('第3次count1=' + count1 + " , " + "count2=" + count2 + " . " + "pre=" + pre + " . " + "count=" + count);
      }
    }
console.log('第n次count1=' + count1 + " , " + "count2=" + count2 + " . " + "pre=" + pre + " . " + "count=" + count);
    if(i === s.length - 1) {
      count2 = s.length - pre;
      count += count1 > count2 ? count2 : count1;
console.log('第4次count1=' + count1 + " , " + "count2=" + count2 + " . " + "pre=" + pre + " . " + "count=" + count);
    }
  }

  return count;
}

一道二进制子串算法,让面试官都解不出来?_第9张图片

第二种JavaScript方法:借助min() 方法:

JavaScript min() 方法

返回值:给定数值中最小的数。如果任一参数不能转换为数值,则返回NaN。

描述

min 是 Math 的静态方法,应该像这样使用:Math.min(),而不是作为你创建的 Math 实例的方法(Math 不是构造函数)。

如果没有参数,结果为Infinity。如果有任一参数不能被转换为数值,结果为 NaN。

什么是JavaScript Math 对象?

定义和用法

min() 方法可返回指定的数字中带有最低值的数字。

Math.min.apply(null, arr)

var array=[2,6,5,8,7];
Math.min.apply(null,array);

一道二进制子串算法,让面试官都解不出来?_第10张图片

/**
 * @param {string} s
 * @return {number}
 */
var countBinarySubstrings = function(s) {
    // 字符串的长度
    const len = s.length;
    // n计数为0,前一个为0,current当前为1。
    let n = 0, pre = 0, current = 1;
    // 循环字符串
    for(let i = 0; i 0) {
       n += Math.min(pre,current);
      }
      pre = current;
      current = 1;
     }
    }
    return n;
};

如果第一数与第二个数相等,比如00,current就要加+1的情况,当前的数量从1变为了2。如果为01,当前的数量给前面的数量pre,当前数量current为1。min取得是数量,pre,current。

一道二进制子串算法,让面试官都解不出来?_第11张图片
一道二进制子串算法,让面试官都解不出来?_第12张图片

function daNum(s) {
    // 字符串的长度
    const len = s.length;
    // 次数为0,前一个为0,current当前为1。
    let n = 0, pre = 0, current = 1;
    console.log('第一n='+n+" , "+"pre="+pre+" , "+"current="+" , "+current);
    // 循环字符串
    for(let i = 0; i

定义和用法

match() 方法可在字符串内检索指定的值,或找到一个或多个正则表达式的匹配。

该方法类似 indexOf() 和 lastIndexOf(),但是它返回指定的值,而不是字符串的位置。

返回值

存放匹配结果的数组。


输出:

world
null
null
world!

第五种JavaScript方法:通用方法

/**
 * @param {string} s
 * @return {number}
 */
var countBinarySubstrings = function(s) {
    // 计算前一个字符连续出现的次数
     let pre = 0
    // 计算后一个字符连续出现的次数
     let cur = 1
    // 每当 pre >= cur 时,既满足条件一次 count++
    // 前面有两个0,后面它自己为1
    // 计数count一开始为0
     let count = 0

    // 循环字符串
     for(let i=1; i= cur, 即可满足条件一次
         if(pre >= cur) {
             count++
         }
     }
     return count
};

看了代码解析应该是懂的了,不过在这里还是口述一下下。

满是条件为01或者是10,就是两者不同,计数加1,出现001,或者是110的情况下,为前面2个0,后面1个1,前面的数量大于后面的数量即为满足一次条件,110的情况也是如此,1的数量为2,0的数量为1。

那么我们来定义一个变量let pre这个变量,这个变量的意思为计算前一个字符串出现的次数,首先这个变量的初始化值为0。如果当前数为 1,那么前面就没有数字,即为它的数量为0。

这里我们需要设置当前数量为1,即出现一个数字,那么数量即为1个。满足条件为前面的数量大于等于后面的数量,即为pre>=cur时,我们计数满足条件加1的情况,定义计数为count,满足条件时,count++

// 计算前一个字符连续出现的次数
let pre = 0
// 计算后一个字符连续出现的次数
let cur = 1
// 每当 pre >= cur 时,既满足条件一次 count++
// 前面有两个0,后面它自己为1
// 计数count一开始为0
let count = 0

注意:计算前一个字符连续出现的次数和计算后一个字符连续出现的次数不同哦!

然后我们给定一个字符串数字,“00110011”,我们需要循环这个字符串中的数字,比较前一个数字和后一个数字是否相等,如果相等,是什么情况呢?如:00或者是11的情况下,当前数cur就要加1。

如果出现不一样的字符时,即情况:10或者是01这些情况,那么计算前一个字符连续出现的次数从0变为1,它有数字,即开始有次数了。把当前cur的次数赋值给pre(计算前一个字符连续出现的次数)。看着01和10的情况,当前cur的次数赋值为1。

满足条件,有人问了,那么001的情况或者是110或者是1100或者是0011或者是111000或者是000111或者是1010等情况下呢?

即这些情况满足如下:计算前一个字符连续出现的次数大于等于计算后一个字符连续出现的次数,即为pre>=cur的条件下满足,计数情况count++,循环字符串后,返回我们需要的count计数。

写在最后:

好了,本章已结束,本质上就是用法不同,解法不同而已!多多打印理解即可!

你可能感兴趣的:(前端数据结构与算法系列,其他分类,算法,字符串,javascript)