一道二進位制子串演算法讓面試官都解不出來?

程式設計師哆啦A夢發表於2020-11-12

演算法題目:

給定一個字串 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”呢?

這道題目,難度係數為簡單題目,涉及到的知識點為字串。

JavaScript的解法:

給定函式體:

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

};

題目理解:

通過看看這兩個示例,字串 s 給的都是二進位制數,要求計算具有相同數量 0 和 1 的非空(連續)子字串的數量,這句話裡面的條件有三個:

第一 不為空,非空(連續)

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

第三 0 和 1 要是連續出現的子字串的數量

描述:

如果遇到10或者是01的情況,則說明連續的1或者是連續的0斷了,那麼可以拿到前面連續1或者是連續0的數量,然後再查詢後面連續1或者是連續0的數量,作比較看看有多少個符合的子串。

目錄:

file

第一種JavaScript:

``` var countBinarySubstrings = function(s) { // 前面一個數,和當前數 let pre=0, count=0, count1=0, count2=0; // 迴圈字串 for(let i = 1; i < s.length; i++) { // 遇到10或01的情況,則說明連續的1或連續的0斷了 if(s[i] !== s[pre]) { if(count1 === 0) { // 拿到前面連續1或0的數量 count1 = i - pre; pre = i; } else { 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; } ```

第二種JavaScript:

/** * @param {string} s * @return {number} */ var countBinarySubstrings = function(s) { // 字串的長度 const len = s.length; // 次數為0,前一個為0,current當前為1。 let n = 0, pre = 0, current = 1; // 迴圈字串 for(let i = 0; i<len; i++){ if(s[i] === s[i+1]) { current++; }else { // 比如00011,就有2組 if(pre > 0) { n += Math.min(pre.current); } pre = current; current = 1; } } return n; };

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);

file

第三種JavaScript:

/** * @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); let total = 0; for(let i=0; i<res.length-1; i++){ total += Math.min(res[i], res[i+1]); } return total; }; 如何使用 min() 來返回指定數字中帶有最低值的數字:

```

document.write(Math.min(5,7) + "
") document.write(Math.min(-3,5) + "
") document.write(Math.min(-3,-5) + "
") document.write(Math.min(7.25,7.30))

``` 輸出:

5 -3 -5 7.25

解題規律

file

  • 000111必定有三個子串
  • 00011必定有兩個子串
  • 0111必定有1個子串

以此類推, 每兩組資料之間長度最短的值為子串的數量 把字串按數字分組切割,如:['00', '11', '00', '11'] 但是如果 是 1010100 這種,怎麼解釋才合理呢?

``` /** * @param {string} s * @return {number} */ 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; } ```

程式碼註解

``` /** * @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<s.length; i++) {
// 如果前一個和後一個相等
     if(s[i] === s[i-1]) {
// 本身當前它自己的數為1,那麼兩者相等,這個數就+1,為2
         cur++
// 00
     } else {
// 當出現不一樣的字元時,現任變前任,現任重新計數
// 01,001,10,101,不一樣的前後,10,01
// 請一個數字的數量為1,後一個數字的數量為1
       pre = cur
// 01,10, 當前數還是1
       cur = 1
     }

// 001, 110, 010, 101,
// 只要  pre >= 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計數。

總結:

好了,本章已結束,如果還有什麼不懂的,請在下方評論進行商議!

❤️ 不要忘記留下你學習的腳印 [點贊 + 收藏 + 評論]

大前端開發,定位前端開發技術棧部落格,PHP後臺知識點,web全棧技術領域,資料結構與演算法、網路原理等通俗易懂的呈現給小夥伴。謝謝支援,承蒙厚愛!!!


請點贊!因為你們的贊同/鼓勵是我寫作的最大動力!

相關文章