Dies ist ein relativ optimierter naive Algorithmus. Sie transformieren zuerst jede Sequenz in eine Menge all ihrer Ngramme. Dann schneiden Sie alle Mengen und finden das längste Ngram in der Kreuzung.
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
Während dies durch kurze Sequenzen führt, ist dieser Algorithmus bei langen Sequenzen äußerst ineffizient. Wenn Ihre Sequenzen lang sind, können Sie eine Heuristik hinzufügen, um die größtmögliche ngram-Länge zu begrenzen (d. H. Die längste mögliche gemeinsame Teilzeichenfolge). Ein offensichtlicher Wert für eine solche Heuristik kann die Länge der kürzesten Sequenz sein.
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
Dieses noch zu lange dauern kann (oder Ihre Maschine aus RAM laufen lassen), so dass Sie vielleicht einige optimalen Algorithmen (siehe den Link ich links in meinem Kommentar zu Ihrer Frage) lesen über wollen.
aktualisieren
die Anzahl der Saiten zu zählen, wobei jedes ngram
from collections import Counter
sequences = ["brownasdfoersjumps",
"foxsxzxasis12sa[[#brown",
"[email protected];"]
seqs_ngrams = map(allngram, sequences)
counts = Counter(chain.from_iterable(seqs_ngrams))
tritt Counter
eine Unterklasse von dict
ist, so seine Instanzen ähnliche Schnittstellen haben:
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,
...
Sie kann die Anzahl filtern, um Teilstrings zu hinterlassen, die mindestens inauftretenStrings: {string: count for string, count in counts.items() if count >= n}
See [Längste gemeinsame Teilfolge umsetzungs Python] (http://stackoverflow.com/questions/25930671/longest-common-subsequence-implementation-python). –
@ WiktorStribiżew Meine Frage ist ganz anders als das, was Sie kommentiert. Er versucht nur zwei Strings zu vergleichen, was ziemlich einfach ist, da ich mehrere Strings verwenden muss, um ein gemeinsames Element zu finden, das mehr als einmal vorkommt. – Elisha512
Der naive Weg wäre, das kürzeste Wort zu wählen und nach all seinen Ngrammen mit zunehmender Größe in allen anderen Strings zu suchen. –