2016-11-11 115 views
1

我正在寫一個python腳本,我有多個字符串。如何找到多個字符串中最長的公共子字符串?

例如:

x = "brownasdfoersjumps" 
y = "foxsxzxasis12sa[[#brown" 
z = "[email protected];" 

在所有這三個字符串,它們都有一個共同的子字符串,它是brown。我想用我想創建字典的方式進行搜索:

dict = {[commonly occuring substring] => 
      [total number of occurrences in the strings provided]} 

這樣做的最佳方法是什麼?考慮到每次我會有超過200個字符串,那麼做一個簡單/有效的方法是什麼?

+0

請參見[最長的公共子序列實現-python](http://stackoverflow.com/questions/25930671/longest-common-subsequence-implementation-python)。 –

+0

@WiktorStribiżew我的問題與您所評論的完全不同。他試圖只比較兩個字符串,這很簡單,因爲我需要使用多個字符串來查找不止一次出現的公共元素。 – Elisha512

+0

天真的方法是選擇最短的單詞並搜索所有其他字符串中所有尺寸增加的ngram。 –

回答

1

這是一個相對優化的樸素算法。您首先將每個序列轉換爲一組所有的ngram。然後你交集所有集合並找到交集中最長的ngram。

from functools import partial, reduce 
from itertools import chain 
from typing import Iterator 


def ngram(seq: str, n: int) -> Iterator[str]: 
    return (seq[i: i+n] for i in range(0, len(seq)-n+1)) 


def allngram(seq: str) -> Iterator[str]: 
    lengths = range(len(seq)) 
    ngrams = map(partial(ngram, seq), lengths) 
    return set(chain.from_iterable(ngrams)) 


sequences = ["brownasdfoersjumps", 
      "foxsxzxasis12sa[[#brown", 
      "[email protected];"] 

seqs_ngrams = map(allngram, sequences) 
intersection = reduce(set.intersection, seqs_ngrams) 
longest = max(intersection, key=len) # -> brown 

雖然這可能讓你通過短序列,但是這種算法在長序列上效率極低。如果序列很長,則可以添加試探法來限制最大可能的ngram長度(即最長可能的常見子字符串)。這種啓發式的一個顯而易見的值可能是最短序列的長度。

def allngram(seq: str, minn=1, maxn=None) -> Iterator[str]: 
    lengths = range(minn, maxn) if maxn else range(minn, len(seq)) 
    ngrams = map(partial(ngram, seq), lengths) 
    return set(chain.from_iterable(ngrams)) 


sequences = ["brownasdfoersjumps", 
      "foxsxzxasis12sa[[#brown", 
      "[email protected];"] 

maxn = min(map(len, sequences)) 
seqs_ngrams = map(partial(allngram, maxn=maxn), sequences) 
intersection = reduce(set.intersection, seqs_ngrams) 
longest = max(intersection, key=len) # -> brown 

這可能還需要太長(或者讓你的機器運行的RAM),所以你可能想了解一些優化算法(看到你的問題我離開的鏈接在我的評論)。

print(counts) 
Counter({'#': 1, 
     '#b': 1, 
     '#br': 1, 
     '#bro': 1, 
     '#brow': 1, 
     '#brown': 1, 
     '-': 1, 
     '-3': 1, 
     '-34': 1, 
     '-34a': 1, 
     '[email protected]': 1, 
     '[email protected]': 1, 
     '[email protected];': 1, 
     ... 

您:

更新

其中每個NGRAM發生

from collections import Counter 
sequences = ["brownasdfoersjumps", 
      "foxsxzxasis12sa[[#brown", 
      "[email protected];"] 

seqs_ngrams = map(allngram, sequences) 
counts = Counter(chain.from_iterable(seqs_ngrams)) 

Counterdict子類,所以它的情況下,也有類似的接口來計算串的數量可以過濾計數以留下至少在發生的子字符串字符串:{string: count for string, count in counts.items() if count >= n}

+0

謝謝!那就是我正在尋找的東西。 :) – Elisha512

+0

還有一件事。它給了我最長的元素,但是如果我想要查找不止一次出現的所有元素並將其發生次數作爲[sequence] => [occurence]添加到字典中?你能提出一些建議嗎? – Elisha512

+0

@ Elisha512因爲這是另一個,SO政策建議開始一個新的職位。不過,我很樂意幫助你。 –

相關問題