LeetCode.3 無重複字符的最長子串(JS)

先跳到第三題是由於第二題第一眼沒讀懂

1、題目

無重複字符的最長子串:html

給定一個字符串,請你找出其中不含有重複字符的 最長子串 的長度。es6

示例1

輸入: "abcabcbb"
輸出: 3
解釋: 由於無重複字符的最長子串是 "abc",因此其長度爲 3。segmentfault

示例2

輸入: "bbbbb"
輸出: 1
解釋: 由於無重複字符的最長子串是 "b",因此其長度爲 1。服務器

示例3

輸入: "pwwkew"
輸出: 3
解釋: 由於無重複字符的最長子串是 "wke",因此其長度爲 3。函數

請注意,你的答案必須是 子串 的長度,"pwke" 是一個子序列,不是子串。

2、個人答案

由於這道題以前在學校刷題的時候見過相似的,大概記得解法,就先放本身的思路測試

v1.0

/**
* @param {string} s
* @return {number}
*/
var lengthOfLongestSubstring = function(s) {
  let resultLength = 0
  function hasEchoChar(str){
    return new Set(str).size !== str.length ? true : false
  }
  let begin = 0, end = 1;
  while(end <= s.length) {
    if (!hasEchoChar(s.slice(begin, end))) {
      end - begin > resultLength ? resultLength = end - begin : null;
      end++
    } else {
      begin++ 
    }
  }
  return resultLength
};

講解優化

  1. 兩個指針用來遍歷字符串,begin指向當前字符串的頭,end指向當前字符串的下一位spa

    let begin = 0, end = 1
  2. 若是當前字符串不包含重複字符串,則判斷是否更新結果(resultLength)且end++,若是包含,begin++指針

    if (!hasEchoChar(s.slice(begin, end ))) {
      end - begin > resultLength ? resultLength = end - begin : null;
      end++
    } else {
      begin++ 
    }

           上文也說了以前碰到過相似的題,當時理解這部分用了很久,由於常寫的循環都是隻有一個指針變量的,像for(let i = 0; i < num; i++),若是按照慣用思路確定是循環套循環遍歷全部子串,麻煩的丫批,上面這種寫法妙就妙在一次循環中要麼更新begin要麼更新end,縮小了子串的篩選範圍。code

           以示例3中的pwwkew爲例,遍歷的過程以下圖
    便利過程

v2.0

寫完v1.0代碼,開心提交,經過是經過了,可是
v2.0產生的緣由
       我:???怎麼肥四,一頓亂吹而後打敗8%,神仙打架?仔細分析了一下,發現是判斷是否包含重複字符的函數hasEchoChar的問題,在v1.0代碼中我把子串轉化成set結構而後對比先後length是否相等,由於set自動去重的特性,若是相等則沒有重複字符。以此來實現判斷是否包含重複字符。(這麼寫的緣由是上一題作完後去看了es6中Set和Map一章,上一題分析鏈接:我是連接)

function hasEchoChar(str){
    return new Set(str).size !== str.length ? true : false
}

       估計耗時就是耗在了轉化成set結構了,這樣寫自己並沒錯,可是他的做用是判斷一個任意字符串是否含有重複字符,咱們每次判斷的是任意字符串嗎?並非,那就不該該繼續採用這種方法。
       聚光燈打到上文中展現遍歷過程那張圖,有重複字符的是pw->pww和wke->wkew,也就是說,只有每次end++時纔有可能產生重複字符,也便是說咱們只須要判斷上次遍歷的字符串是否包含此次新添加的字符便可,思路理清,代碼以下

/**
 * @param {string} s
 * @return {number}
 */
var lengthOfLongestSubstring = function (s) {
  let resultLength = 0
  let begin = 0, end = 1
  while (end <= s.length) {
    let index = s.slice(begin, end - 1).indexOf(s[end - 1])
    if (index !== -1) {
      begin += (index + 1)
      end++
    } else {
      end - begin > resultLength ? resultLength = end - begin : null;
      end++
    }
  }
  return resultLength
};

       同時作出優化的部分還有找到相同字串的狀況

if (index !== -1) {
  begin += (index + 1)
  end++
}

       在v1.0的代碼中,每次找到相同字符就將begin指到下一個位置,若是還有呢,就再指向下一個位置,很明顯begin能夠一次指到位,即指到與當前子串末位相同字符位置的下一位,同時end++,開始下一輪的嶄新循環。

       至此這道題我已經竭盡所能,執行用時從v1.0的956ms下降到v2.0的132ms,擊敗提交也從8.77%漲到71.88%。可是貪婪的我並不能知足於71.88%,因而去複製了一份耗時最低的答案本身提交,發現耗時140ms甚至比個人耗時還要多?去查了一下(查詢結果),可能緣由爲:一、服務器負載;二、測試用例增長。
       奧~~這樣啊
       那對不起,個人就是最佳答案

3、優秀答案

/**
 *. @param {string} s
 *. @return {number}
 */
var lengthOfLongestSubstring = function(s) {
    let substr = '', maxLength = 0;
    for (var i = 0; i < s.length; i++) {
        let findIndex = substr.indexOf(s[i]);
        if (~findIndex) {
            substr = substr.substring(findIndex + 1);
        }
        substr += s[i];
        if (substr.length > maxLength) {
            maxLength = substr.length;
        }
    }
    return maxLength;
};
  1. 按位取反~
    以前從沒見過~符號,搜了一下,是什麼補碼之類的,應該是大學計算機原理講的東西(流下了悔恨的淚水),感興趣的小夥伴能夠看一下這個js中怎麼理解按位取反?
  2. substring
    功能和slice相同相似,一樣,感興趣的小夥伴能夠看一下MDN中substring的定義

4、路漫漫其修遠兮

相關文章
相關標籤/搜索