2015-07-12 5 views
5

Wie kann ich den folgenden Code erweitern, damit ich alle Instanzen untersuchen kann, in denen ich 2 oder weniger Übereinstimmungen zwischen meiner Teilzeichenfolge und der übergeordneten Zeichenfolge habe?String Regex zwei Fehlanpassungen Python

Substring: SSQP

String-to-Match-to: SSPQQQQPSSSSQQQSSQPSPSQSSQPSSQPPSSSSQPSPSQSSQPSSSSQPSPSQSSQPSSSSQPSPSQ

Hier ist ein Beispiel, wo nur eine mögliche Diskrepanz eingebaut ist:

>>> s = 'SSPQQQQPSSSSQQQSSQPSPSQSSQPSSQPPSSSSQPSPSQSSQPSSSSQPSPSQSSQPSSSSQPSPSQ' 
>>> re.findall(r'(?=(SSQP|[A-Z]SQP|S[A-Z]QP|SS[A-Z]P|SSQ[A-Z]))', s) 
['SSQQ', 'SSQP', 'SSQP', 'SSQP', 'SSQP', 'SSQP', 'SSQP', 'SSQP', 'SSQP'] 

Offensichtlich Einbeziehung der Möglichkeit von zwei Fehlanpassungen in dem obigen Code würde eine Vielzahl von Brute-Force-Typisierung aller möglichen Kombinationen erfordern.

Wie kann ich diesen Code erweitern (oder diesen Code umgestalten), um die Möglichkeit von zwei Fehlanpassungen zu untersuchen?

Darüber hinaus möchte ich meine Ausgabe so ändern, dass ich den numerischen Index zurückgegeben (nicht SSQQ oder SSQP) der genauen Position der Teilzeichenfolge die Zeichenfolge übereinstimmt.

Antwort

5

Sie müssen hier nicht verwenden re Sie itertools Modul verwenden können, Stattdessen und speichern Sie eine Menge Speicher.

können Sie zuerst extrahieren alle Teilketten mit einer Länge von 4 dann vergleichen Sie sie mit Ihren substring und nur diejenigen auswählen, die weniger als 2 Unterschied mit Ihrer substring:

from itertools import izip,islice,tee 

def sub_findre(s,substring,diffnumber): 
    sublen=len(substring) 
    zip_gen=(izip(substring,islice(s,i,i+sublen)) for i in xrange(len(s))) 
    for z in zip_gen: 
     l,z=tee(z) 
     if sum(1 for i,j in l if i==j)>=sublen-diffnumber: 
      new=izip(*z) 
      next(new) 
      yield ''.join(next(new)) 

Demo:

s='SSPQQQQPSSSSQQQSSQPSPSQSSQPSSQPPSSSSQPSPSQSSQPSSSSQPSPSQSSQPSSSSQPSPSQ' 

substring='SSQP' 
print list(sub_findre(s,substring,2)) 

['SSPQ', 'SPQQ', 'QQQP', 'SSSS', 'SSSQ', 'SSQQ', 'SQQQ', 'SSQP', 'PSQS', 'SSQP', 'SSQP', 'SQPP', 'SSSS', 'SSSQ', 'SSQP', 'PSQS', 'SSQP', 'SSSS', 'SSSQ', 'SSQP', 'PSQS', 'SSQP', 'SSSS', 'SSSQ', 'SSQP', 'PSQ'] 

Wenn Sie die Indizes zurückgeben möchten, müssen Sie die Indizes in izip setzen, die Sie itertools.repeat() verwenden können, um den Index mit der Länge von substring zu wiederholen:

from itertools import izip,islice,tee,repeat 

def sub_findre(s,substring,diffnumber): 
    sublen=len(substring) 
    zip_gen=(izip(substring,islice(s,i,i+sublen),repeat(i,sublen)) for i in xrange(len(s))) 
    for z in zip_gen: 
     l,z=tee(z) 
     if sum(1 for i,j,_ in l if i==j)>=sublen-diffnumber: 
      new=izip(*z) 
      next(new) 
      next(new) 
      yield next(new)[0] 

Demo:

print list(sub_findre(s,substring,2)) 
[0, 1, 4, 8, 9, 10, 11, 15, 20, 23, 27, 28, 32, 33, 34, 39, 42, 46, 47, 48, 53, 56, 60, 61, 62, 67] 
+1

In der Tat, reguläre Ausdrücke sind nur das falsche Werkzeug, um insgesamt zu verwenden. Für 2 Fehler von 20, würde es 190 Alternativen im Muster geben. –

+0

Können Sie Indexzahlen zurückgeben, ähnlich wie bei "match.start (0)" - Methode von 200_success? – warship

+0

@warship Beende den Schnitt! – Kasramvd

2

Die kombinatorische Explosion ist nicht , die schlecht für zwei Fehlpaarungen von vier ist.

Zuerst, beachten Sie, dass Sie SSQP selbst weglassen können, da es von all den milderen Fällen abgedeckt wird.

re.findall(r'(?=([A-Z]SQP|S[A-Z]QP|SS[A-Z]P|SSQ[A-Z]))', s) 

So ist die Zahl der Fälle ist

   4! 
C(4, 1) = ––––––––––––– = 4 
      1! * (4 - 1)! 

Für zwei Fehlpaarungen bis zu, die Zahl der Fälle ist

   4! 
C(4, 2) = ––––––––––––– = 6 
      2! * (4 - 2)! 

nämlich

re.findall('(?=(SS..|S.Q.|S..P|.SQ.|.S.P|..QP))', s) 

(To vereinfache die Illustration, ich habe mir die Freiheit genommen . . statt [A-Z])


Schreiben Um die Positionen der Spiele anstelle des Textes der Spiele zu erhalten:

[match.start(0) for match in re.finditer('(?=(SS..|S.Q.|S..P|.SQ.|.S.P|..QP))', s)] 
+0

Ich sehe, was du tust, aber ich habe Substrings wie 10 mal 20 Buchstaben groß, manchmal mehr, es ist sehr schwer für mich, die 're' maßstäblich mit Modul. Vielleicht kann ich da noch etwas anderes für reguläre Ausdrücke benutzen? Ich mag Ihre Formulierung von "kombinatorischer Explosion", aber ich werde diese Terminologie in mein Arsenal stellen :-) – warship

+1

Warum haben Sie dann die Frage nach C (4,2) gestellt, statt nach dem, was Sie eigentlich wollten? Wie viele Fehler von 20 redest du? –

+0

0, 1 oder 2 Fehler in einem Teilstring der Länge 20 sind möglich. Solches kombinatorisch zu schreiben, ist ein königlicher Schmerz. Im OP wollte ich einfach einen MWE bereitstellen. – warship