簡體   English   中英

在兩個(或更多)數組中找到匹配的單元序列的最有效方法是什么?

[英]What's the most efficient way to find matching sequences of cells in two (or more) arrays?

例1

假設我有兩個數組:

('n','v','a','n','i','n','n','v','a','n')
('a','n','n','n','v','a','n','v','n')

我想找到兩者之間的所有匹配序列(可能長於兩個單元長度左右),這些序列不是其他較長匹配的子匹配。 這是我看到的匹配:

('n','n','v','a','n') =數組1中的位置5和數組2中的位置3

數組1:('n','v','a','n','i', 'n','n','v','a','n'

數組2:('a','n', 'n','n','v','a','n' ,'v','n')

例2

('n','v','a','n','i','n','n','v','i','n')
('a','n','i','n','p','v','i','n','v','n')

在這里,我們有不止一個序列,但它們更短如下:

('a','n','i','n') = arraay 1中的位置2和數組2中的位置0

('v','i','n') =數組1中的位置7和數組2中的位置5

數組1:('n','v', 'a','n','i','n' ,'a', 'v','i','n'

數組2:( 'a','n','i','n' ,'p', 'v','i','n' ,'v','n')

摘要

兩個示例中都有多個匹配項,但它們都存在於至少一個數組中的較大匹配項中。

那么什么是最有效的(低內存和高速的平衡,想想移動設備)代碼可以實現這一目標? JavaScript代碼示例會很棒!

如果兩個數組的長度為mn ,我認為在一般情況下你不可能比O(mn)做得更好。 假設您的陣列具有交替a s但是具有其他不同的字符,就像這樣

[a, b, a, c, a, d, a, e, a, f, a, g]
[a, h, a, i, a, j, a, k, a, l, a, m]

匹配數是(m/2)*(n/2) 如果要全部找到它們,您的算法最多可以是O(mn)

你可以在O(mn)時間內完成如下操作。 想象一下,將一個數組滑過另一個數組,如下所示:

[a, b, c, d, e]
            [f, g, h, i, j]

   [a, b, c, d, e]
            [f, g, h, i, j]

      [a, b, c, d, e]
            [f, g, h, i, j]

                  ...
                        [a, b, c, d, e]
            [f, g, h, i, j] 

m + n - 1可能的位置。 對於每個位置,您必須迭代對齊的字符對(最差的是min(m, n)這些對)並找到最長的匹配字符鏈。 這有時間復雜性

O((m + n) * min(m, n)) = O(mn)

該解決方案的缺點在於,所花費的時間實際上僅取決於陣列的長度,而不取決於內容。 例如,即使數組相等,它仍然需要O(nm)時間(當它顯然只需要O(n)時間來檢查它並返回一個答案時)。 如在另一個答案中所指出的,如果匹配序列的數量很少,則有更多更聰明的解決方案將花費更少的時間。

這是我在一般LCS, O(mn)時間和空間版本上的JavaScript嘗試。 由於我們逐行進行,因此只需重復使用兩行就可以減少空間,完成后將第二行復制到第一行。

var example1 = [['n','v','a','n','i','n','n','v','a','n']
               ,['a','n','n','n','v','a','n','v','n']],

    example2 = [['n','v','a','n','i','n','n','v','i','n']
               ,['a','n','i','n','v','i','n','v','n']];

function f(as){
  var M = new Array(as[0].length),
      result = [];

  for (var i=0; i<as[0].length; i++){
    M[i] = new Array(as[1].length).fill(0);

    for (var j=0; j<as[1].length; j++){
      if (as[0][i] == as[1][j]){
        M[i][j] = M[i-1] && M[j-1] ? 1 + M[i-1][j-1] : 1;
      }
      if ((i == as[0].length - 1 || j == as[1].length - 1) && M[i][j] > 2){
        result.push([i - M[i][j] + 1,j - M[i][j] + 1,M[i][j]]);
      } else if (i > 1 && j > 1 && M[i][j] < M[i-1][j-1] && M[i-1][j-1] > 2){
        result.push([i - M[i-1][j-1],j - M[i-1][j-1],M[i-1][j-1]]);
      }
    }
  }

  return result;
}

console.log(JSON.stringify(f(example2))); // [[2,0,4],[6,3,4]]

這是兩個字符串AB O(n) O(n + k)解,其長度總和為n,並且具有k個這樣的最大匹配子串:

  1. 在兩個字符串AB上構建一個通用后綴樹 (這只是單個字符串A$B#上的普通后綴樹,其中$#是單個字符,不會出現在AB任何位置。)這可以使用例如Ukkonen算法在O(n)時間內完成。
  2. 通過這個樹執行自下而上的DFS,在每個節點上做兩件事:
    • 確定和記錄,是否存在對應於的后綴任何葉A此節點下,以及是否存在對應於的后綴任何葉B此節點下。 (練習:如何回答這個問題?)
    • 如果存在這兩種葉子, 並且對於任何子節點都不是這樣,則報告與該節點對應的子串作為解決方案。 (如果條件也適用於某個子節點,則對應於此節點的子字符串是與該子節點對應的子字符串的子字符串,並且您只需要最大子字符串。)

這也適用於小數字> = 3的字符串:計算並存儲在當前節點下面有葉子的輸入字符串集合 ,並在此集合變滿時“激活”。

暫無
暫無

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

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