簡體   English   中英

更好的大 O 更差的實時執行

[英]Better big O worse real time execution

所以我正在學習算法課程,它要求實現以下 function:

編寫一個名為 findLongestSubstring 的 function,它接受一個字符串並返回最長的 substring 的長度以及所有不同的字符。

findLongestSubstring('') // 0

findLongestSubstring('bbbbbb') // 1

findLongestSubstring('longestsubstring') // 8

並說它必須具有o(n)的時間復雜度。 所以我想不出 ao(n) 解決方案,而是一個假定的 (n^2)... 我天真的解決方案:

function findLongestSubstring(str: string): number {
  const array = str.split("");
  let maxLength = 0;

  for (let i = 0; i < array.length; i++) {
    const letterArray = [array[i]];

    for (let j = i + 1; j < array.length; j++) {
      if (letterArray.find(e => e === array[j])) {
        if (letterArray.length > maxLength) maxLength = letterArray.length;
        break;
      } else {
        letterArray.push(array[j])
        if (j === array.length - 1) {
          if (letterArray.length > maxLength) maxLength = letterArray.length;
          break;
        }
      }
    }

  }

  return maxLength;
}

然后我去查看他的解決方案:

function findLongestSubstring(str: string) {

  let longest = 0;
  let seen = {};
  let start = 0;
 
  for (let i = 0; i < str.length; i++) {
    let char = str[i];
    if (seen[char]) {
      start = Math.max(start, seen[char]);
    }
    // index - beginning of substring + 1 (to include current in count)
    longest = Math.max(longest, i - start + 1);
    // store the index of the next char so as to not double count
    seen[char] = i + 1;
  }
  return longest;
}

所以我同意我的具有嵌套 for 循環的解決方案似乎是 O(n2) 而他的解決方案是 o(n)。 但是當我用一個非常大的字符串測試這兩種算法時,令我驚訝的是我的算法確實更快而且我不知道為什么......有人可以啟發我嗎? 我測試的方式:

  test("really big string to test bigO", () => {
    const alphabet = "abcdefghijklmnopqrstuvwxyz"

    const randomLettersArray = Array.from({ length: 100000000 }, () => alphabet[Math.floor(Math.random() * alphabet.length)]);
    const randomBigString = randomLettersArray.join("")


    const start = Date.now();

    findLongestSubstring(randomBigString);

    const end = Date.now();
    console.log(`Execution time: ${end - start} ms`);
  })

您的嵌套循環解決方案看起來可能需要 O(n 2 ) 時間,但實際上它受到最長可能有效 substring 的限制,並且受到alphabet 大小的限制。

您的測試使用了一個小字母表——只有 26 個字符。 在這種情況下,您的內部循環最多有 26 次迭代,並且在某些環境中這可能比其他算法更快。

如果您要使用更大的字母表——比如 10000 個字符左右,那么您的算法會慢得多,但另一個算法根本不會慢很多。

讓我們考慮在連續位置結束的所有最長子串(滿足唯一性條件):

l -> l
lo -> lo
lon -> lon
long -> long
longe -> longe
longes -> longes
longest -> longest
longests -> ts
longestsu -> tsu
longestsub -> tsub
longestsubs -> ubs
longestsubst -> ubst
longestsubstr -> usbtr
longestsubstri -> ubstri
longestsubstrin -> ubstrin
longestsubstring -> ubstring

您會注意到第一個字符的索引永遠不會減少。 每次添加一個新字符時,起始索引要么不變,要么移過 substring 中相同字符的 position。

所以下面的循環可以做:

the substring is empty
for all ending positions in the string:
  append the new character to the substring
  move the starting position past the same character in the substring, if any

但是我們還沒有完成:如果在 substring 中找到新字符,則起始 cursor 將停在那里; 但如果沒有找到,我們就需要白遍遍遍整個substring,這會使復雜度成二次方。

所以我們需要一個額外的設備來告訴我們 substring 是否包含新字符而不需要遍歷 substring 由於字母表的大小有限,我們可以使用布爾數組作為計數器。 最初所有的布爾值都是零; 當一個字符進入/退出 substring 時,它在數組中的條目將被設置/重置。

因此整個搜索可以在線性時間內執行。 當然解就是這個過程中找到的最長字符串的長度。

all booleans are initially reset
the substring is empty
for all ending positions in the string:
  append the new character to the substring
  if the boolean for this character is set:
     move the starting position past the same character in the substring,
       while resetting the booleans of all characters met
  set the boolean for this character
  update the longest length so far

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM