1. 程式人生 > >前端演算法題 | 這道題效率最高的演算法,你可能不知道?

前端演算法題 | 這道題效率最高的演算法,你可能不知道?

 

 

尋找最長的不含有重複字元的子串

可能看標題不會明白這個題到底什麼意思,來看看下面的例子:

 

abcabcbb  ➡  abc ➡  3

bbbb  ➡  b  ➡  1

pwwkew  ➡  wke ➡  3

看了栗子是不是明白了呢?

其實需求很簡單,實現的方法也很多,不過在這裡我要來寫一種效率最高的演算法,只需要一次迴圈就可解決:

function findNoRepeatMaxLenStr (str) {

      let lastPositions = {}

      let start = 0

      let maxLen = 0

    

      for (let i=0; i<str.length; i++) {

            const s = str[i]

            if (lastPositions[s] !== 'undefined' && lastPositions[s] >= start) {

                  start = lastPositions[s] + 1

            }

            if (i - start + 1 > maxLen) {

                  maxLen = i - start + 1

            }

        lastPositions[s] = i

      }

      return maxLen

}

 

// test

console.log(findNoRepeatMaxLenStr('abcabcbb'))  // 3

那麼看完程式碼,請自己先胡思亂想一下,能看得懂不?

行了,看到這我就知道你沒看懂,那麼來解釋一下吧。

思路是這樣的,假如下面的圖形是一個字串,每個格子代表一個字元:

此時咱們開始使用 for 迴圈掃描整個字串,當掃描到 x(x 代表任意位置的任意的字串)的時候,那麼咱們應該怎麼做呢?

首先要記錄一個 start 起始位置,當然一開始就是 0 了,那麼 start 在迴圈中不僅僅只是表示字串從 0 開始,還表示當前不含有最長重複子串的開始,那麼咱們要做的事情就一件:保證從 start 到 x 之間沒有重複的字串,再說的通俗點就是看檢查 start 到 x 之間有沒有重複的 x 。

那麼怎麼做到檢查這個動作呢?

這個時候 lastPositions 就派上用場了。當迴圈到 x 的時候,記錄一下這個 x 最後一次出現的位置在哪裡。

那麼記錄完畢之後,當進行檢查 start 到 x 之間 是否有重複的 x 的時候,咱們就去問 lastPositions[x],此時會有2種情況:

  • 第一種情況是 x 從來沒有出現過或者出現在了 start 之前;

  • 第二種情況是 x 出現在 start 到 x 中間。

那麼對於第一種情況,咱們不用去管。而第二種情況自然是不滿足條件的情況了,此時,咱們就要更新 lastPositions[x],將這個 x 的位置更新為 lastPositions[x] + 1。

 

總結下來就是:

  • lastPositions[x] 不存在或 < start  滿足條件,無需操作

  • lastPositions[x] 存在並且 >= start  不滿足條件,更新 lastPositions[x]++

那麼在結合上面的程式碼,邏輯就清晰了,唯一有些繞圈圈的地方就是第二個 if 中的 +1 操作,原因就是字串的索引是從 0 開始的,那麼假如第一個為 x 滿足條件,實際索引是0,那麼長度應該是 0 + 1 = 1。

 

最後,這道演算法題的出處來自:

  • https://leetcode.com/problems/longest-substring-without-repeating-characters/description/

  • 裡面有各種各樣的實現方法,可以作為參考。