如何定义不连续多列数据放在数组里取不重复数值_一道二进制子串算法,让面试官都解不出来?...

bca7c0242ced8c87d96b290d1f1ae44f.png
92cc32a7220046242ebd2550f0a122ee.png

算法题目:

给定一个字符串 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”呢?

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

给定函数体如下:

a185ec3081eb2c7d157a3339e6636311.png

题目理解:

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

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

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

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

描述:

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

多种解题思路:

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

614cf9473e9f1634d33fab188794ece6.png

第一种JavaScript方法:按照前后数量判断:

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

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

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

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

ad1f6ec8391336a5c144848645a89372.png

看完代码得知,返回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

3fdba3e78e739617cd8d8b98dce6a8f6.png
cb9b5c069ada1ffb9315e48d39ef79ed.png

其实可以看出代码中一开始都会走count1===0这条路线。pre为递增下标数,比较当前值与前一个值的情况。每次(pre=i)

ea00a56ffa735a7707a865c612818d5c.png
0d77793929da3fa5e1d37667de0c9c65.png

看图带入,解释一下,本质,如果循环中两者不相等

(s[i] !== s[pre])走里面的情况,如果前者和后者比较为相等情况,不走if(s[i] !== s[pre])里的代码块,知道不相等的情况。最后一个数到了最后即尾部,满足条件(i === s.length - 1),执行其中的代码块

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;}
282a38af1e45c1b87a88e0396e8baaac.png

第二种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);
ab01b62085681dc05b21a1861d32e5fb.png
var array=[2,6,5,8,7];Math.min.apply(null,array);

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

df08562cf8fd09152e6b99743b68849d.png
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 0) {       n += Math.min(pre,current);       console.log('第三n='+n+" , "+"pre="+pre+" , "+"current="+" , "+current);      }      pre = current;      current = 1;      console.log('第四n='+n+" , "+"pre="+pre+" , "+"current="+" , "+current);     }    }    console.log('第五n='+n+" , "+"pre="+pre+" , "+"current="+" , "+current);    return n;};

第三种JavaScript方法:借助min() 方法与push() 方法:

total为计数,res存储相邻连续字符串的个数

JavaScript push() 方法

定义和用法

push() 方法可向数组的末尾添加一个或多个元素,并返回新的长度。

语法
arrayObject.push(newelement1,newelement2,....,newelementX)

e8f00163ae450ef0fc55ea7a4cc6a83f.png


返回值

把指定的值添加到数组后的新长度。

push() 方法可把它的参数顺序添加到 arrayObject 的尾部。它直接修改 arrayObject,而不是创建一个新的数组。push() 方法和 pop() 方法使用数组提供的先进后出栈的功能。

/** * @param {string} s * @return {number} */var countBinarySubstrings = function(s) {    // res 存储相邻连续字符串的个数    let res = [];    // 字符串    let temp = s[0];    let count = 0;    for(let i of s) {     // 循环字符串     if(i !== temp) {      res.push(count);      temp = i;      count = 0;     }     count++;    }    res.push(count);    // 计数为0    let total = 0;    // 计数为0    for(let i=0; i


如何使用 min() 来返回指定数字中带有最低值的数字:

输出:

5  -3  -57.25

第四种JavaScript方法:借助match() 方法

ffff20d2629aacfc2b8e90bda9fb348b.png


000111必定有三个子串

00011必定有两个子串0111必定有1个子串


以此类推, 每两组数据之间长度最短的值为子串的数量

把字符串按数字分组切割,如:['00', '11', '00', '11']

但是如果 是 1010100 这种,怎么解释才合理呢?

解题思路:

把字符串按数字分组切割,如:['00', '11', '00', '11'],相邻的两组数据组合,长度较短的数据长度即为这组数据可能的数据次数

/** * @param {string} s * @return {num} */var countBinarySubstrings = function(s) {  let num = 0;  const arr = s.match(/0+|1+/g);   // let n = 0, arr = s.match(/([1]+)|([0]+)/g)    // 把字符串切割成['00', '11', '00', '11']这样的数组  for(let i = 0, len = arr.length; i < len - 1; i++){    num += Math.min(arr[i].length, arr[i+1].length);         // 相邻比较,长度更短的则为这一组的出现次数  }  return num;}

定义和用法

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

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

返回值

存放匹配结果的数组。

bca814533cdfb406233e48b7854dd89d.png


第五种JavaScript方法:通用方法

cf53e0e3662b2468508df3f7525cdbd1.png

看了代码解析应该是懂的了,不过在这里还是口述一下下。
满是条件为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++

8e9df152be81ba8ce9c73bdb4d9634c9.png

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

然后我们给定一个字符串数字,“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计数。

最后,我自己是一名从事了多年开发的JAVA老程序员,今年年初我花了一个月整理了一份最适合初学者学习的java学习干货,可以送给每一位喜欢java的小伙伴,想要获取的可以关注我的头条号并在后台私信我:交流,即可免费获取。

5af84ace1d976f5ea7a5d02a0131ed17.png
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值