Überprüfen Sie, ob es eine isomorphe Teilzeichenfolge gibt

12

Diese Frage ist eine Erweiterung von Check if words are isomorphs und kopiert den ersten Teil davon, um die Definition eines Isomorphs zu erhalten.

Zwei Wörter sind Isomorphe, wenn sie das gleiche Muster von Buchstabenwiederholungen aufweisen. Zum Beispiel beide ESTATEund DUELEDhaben Musterabcdca

ESTATE
DUELED

abcdca

da die buchstaben 1 und 6 gleich sind, sind die buchstaben 3 und 5 gleich und nichts weiter. Dies bedeutet auch, dass die Wörter durch eine Substitutionschiffre miteinander verbunden sind, hier mit der Übereinstimmung E <-> D, S <-> U, T <-> E, A <-> L.

Bei zwei gegebenen Strings X und Y, wobei X nicht länger als Y ist, besteht die Aufgabe darin, zu bestimmen, ob es einen Teilstring von Y gibt, der mit X isomorph ist.

Eingabe: Zwei nicht leere Buchstabenfolgen von a..zA..Z, eine Zeichenfolge pro Zeile. Dies wird von der Standardeingabe kommen.

Ausgabe Eine Teilzeichenfolge der zweiten Zeichenfolge, die mit der ersten Zeichenfolge isomorph ist, sofern eine solche vorhanden ist. Ansonsten geben Sie "Nein!"

Regeln Ihr Code muss in der Gesamtlänge der Eingabe in linearer Zeit ausgeführt werden.

Ergebnis Ihre Punktzahl ist die Anzahl der Bytes in Ihrem Code. Wenigste Bytes gewinnt.


Beispiel für Ein- und Ausgabe

adca
ddaddabdaabbcc

dabd

Trinkgeld

Es gibt mindestens eine nicht so komplizierte, praktisch schnelle und lineare zeitliche Lösung für dieses Problem.


quelle
@AlexA. Ich denke, jemand hat mir gesagt, wenn Sie die Laufzeit / Komplexität einschränken, sollte es eine Code-Herausforderung sein. Ich ändere es gerne, wenn das natürlich falsch ist.
7
Wenn die Laufzeit durch eine Regel begrenzt ist und die Wertung nicht beeinflusst, passt Code-Golf besser als Code-Challenge.
Dennis
lineare Zeit bedeutet, dass es O (m + n) und nicht O (mxn) oder O (mx (nm)) sein muss, wobei m, n die Länge der ersten und zweiten Saite sind?
Einige Benutzer
@someuser Ja, es bedeutet O (m + n).
1
@BetaDecay Siehe "Bei zwei gegebenen Strings X und Y, wobei X nicht länger als Y ist, besteht die Aufgabe darin, zu bestimmen, ob es einen Teilstring von Y gibt, der mit X isomorph ist."

Antworten:

8

Python 2 338 326 323 321 310 306 297 293 290 289 280 279 266 264 259 237 230 229 226 223 222 220 219 217 ( 260 238 231 228 225 223 221 220 218 0 mit Beendigungsstatus)

exec'''s=raw_input()
S=[M-s.rfind(c,0,M)for M,c in enumerate(s)]
k=0
j=x=%s
while k<=M+x:
 if S[k]>j<W[j]or S[k]==W[j]:
    k+=1;j+=1;T+=[j]
    if j-L>x:print s[k-j:k];z
 else:j=T[j]
'''*2%('-1;T=[0];W=S;L=M',0)
print'No!'

Der Algorithmus ist eine Variation von KMP und verwendet einen indexbasierten Test für die Zeichenübereinstimmung. Die Grundidee ist, dass wir, wenn wir eine Fehlpaarung an der Position erhalten, X[i]auf den nächstmöglichen Platz für eine Übereinstimmung zurückgreifen können, wobei das längste Suffix davon X[:i]isomorph zu einem Präfix von ist X.

Wenn Sie von links nach rechts arbeiten, weisen Sie jedem Zeichen einen Index zu, der dem Abstand zum letzten Vorkommen dieses Zeichens entspricht. Wenn es kein vorheriges Vorkommen gibt, nehmen Sie die Länge des aktuellen Zeichenfolgepräfix. Beispielsweise:

MISSISSIPPI
12313213913

Um zu testen, ob zwei Zeichen übereinstimmen, vergleichen wir Indizes und passen sie entsprechend an Indizes an, die länger als die aktuelle (Unter-) Zeichenfolge sind.

Der KMP-Algorithmus wird etwas vereinfacht, da beim ersten Zeichen keine Abweichung festgestellt werden kann.

Dieses Programm gibt die erste Übereinstimmung aus, falls eine vorhanden ist. Ich verwende einen Laufzeitfehler, um im Falle einer Übereinstimmung zu beenden, aber der Code kann leicht geändert werden, um auf Kosten einiger Bytes sauber zu beenden.

Hinweis: Für die Berechnung von Indizes können wir verwendenstr.rfind (im Gegensatz zu meinem früheren Ansatz mit einem Wörterbuch) eine lineare Komplexität verwenden und gehen davon aus, dass str.rfinddie Suche am Ende beginnt (was die einzig vernünftige Implementierungsoption zu sein scheint) - für jedes Zeichen im Alphabet Müssen wir nie denselben Teil der Zeichenkette zweimal durchlaufen, so gibt es eine Obergrenze für (Alphabetgröße) * (Zeichenkettengröße) Vergleiche.

Da der Code während des Golfspiels ziemlich verschleiert wurde, finden Sie hier eine frühere (293 Byte) Lösung, die etwas einfacher zu lesen ist:

e=lambda a:a>i<W[i]or a==W[i]
exec('s=raw_input();S=[];p={};M=i=0\nfor c in s:S+=[M-p.get(c,-1)];p[c]=M;M+=1\nW=S;L=M;'*2)[:-9]
T=[0]*L
k=1
while~k+L:
 if e(W[k]):i+=1;k+=1;T[k]=i
 else:i=T[i]
m=i=0
while m+i<M:
 if e(S[m+i]):
    if~-L==i:print s[m:m+L];z
    i+=1
 else:m+=i-T[i];i=T[i]
print'No!'

Die eFunktion testet die Gleichwertigkeit von Zeichen. Die execAnweisung weist Indizes zu und führt einige Variableninitialisierungen durch. Die erste Schleife wird ausgeführtX die Fallback-Werte, und die zweite Schleife führt die Zeichenfolgensuche durch.

Update: Hier ist eine Version, die auf Kosten eines Bytes sauber beendet wird:

r='No!'
exec'''s=raw_input()
S=[M-s.rfind(c,0,M)for M,c in enumerate(s)]
k=0
j=x=%s
while k<=M+x:
 if S[k]>j<W[j]or S[k]==W[j]:
    k+=1;j+=1;T+=[j]
    if j-L>x:r=k=s[k-j:k]
 else:j=T[j]
'''*2%('-1;T=[0];W=S;L=M',0)
print r
Mitch Schwartz
quelle
Ich denke, Sie können ein Byte speichern, indem Sie Python3 ausführen. r=raw_inputr = inputspart 4 Bytes und die gedruckten Parens kosten nur 3.
Maltysen
@Maltysen danke für den Kommentar. Ich habe Python 3 beim Schreiben nicht berücksichtigt, aber was die Kosten und Einsparungen angeht, fallen zusätzliche 2-Byte-Kosten an, da Sie in Python 3 keine Leerzeichen und Tabulatoren für Einrückungen mischen können.
Mitch Schwartz
@Maltysen nur zum nachfassen, das Thema ist jetzt nicht mehr Tabs sondern Klammern dafür exec.
Mitch Schwartz
Das ist eine wirklich gute Antwort! Ich freue mich darauf, es jetzt in cjam zu sehen :)
6

Python 3, 401 Bytes

import string,itertools
X=input()
Y=input()
x=len(X)
t=[-1]+[0]*~-x
j=2
c=0
while j<x:
 if X[j-1]==X[c]:c+=1;t[j]=c;j+=1
 elif c>0:c=t[c]
 else:t[j]=0;j+=1
s=string.ascii_letters
*b,=map(s.find,X)
for p in itertools.permutations(s):
 m=i=0
 while m+i<len(Y):
  if p[b[i]]==Y[m+i]:
   if~-x==i:print(Y[m:m+x]);exit()
   else:i+=1
  else:
   if-1<t[i]:m+=i-t[i];i=t[i]
   else:i=0;m+=1
else:print("No!")

Dies ist immer noch größtenteils ungolfed, aber ich denke, es sollte funktionieren. Der Kernalgorithmus ist KMP und ein zusätzlicher Faktor, der für die Größe des Alphabets von Bedeutung ist (was in Ordnung ist, da das Alphabet konstant ist). Mit anderen Worten, dies ist / sollte ein völlig unpraktischer linearer Algorithmus sein.

Hier sind einige Anmerkungen, die bei der Analyse helfen sollen:

# KMP failure table for the substring, O(n)
t=[-1]+[0]*~-x
j=2
c=0
while j<x:
 if X[j-1]==X[c]:c+=1;t[j]=c;j+=1
 elif c>0:c=t[c]
 else:t[j]=0;j+=1

# Convert each char to its index in a-zA-Z, O(alphabet * n)
s=string.ascii_letters
*b,=map(s.find,X)

# For every permutation of letters..., O(alphabet!)
for p in itertools.permutations(s):
 # Run KMP, O(n)
 m=i=0
 while m+i<len(Y):
  if p[b[i]]==Y[m+i]:
   if~-x==i:print(Y[m:m+x]);exit()
   else:i+=1
  else:
   if-1<t[i]:m+=i-t[i];i=t[i]
   else:i=0;m+=1
else:print("No!")

Zum Testen können Sie durch sein kleineres Alphabet als ersetzen string.ascii_letters.

Sp3000
quelle
2

APL (Dyalog) , 32 Bytes

Dies ist eine Infix-Funktion, die X als linkes Argument und Y als rechtes Argument verwendet.

{(s,⊂'No!')⊃⍨(⍳⍨¨s←⍵,/⍨≢⍺)⍳⊂⍳⍨⍺}

Probieren Sie es online!

{... } anonymes Lambda wo und repräsentiere die Argumente (X und Y)

⍳⍨⍺ɩ ndex selfie of X ( Indexe des ersten Vorkommens von Elementen von X in X)

 umschließen, damit wir das gesamte Muster suchen können

()⍳Ɩ ndex des ersten Auftretens davon in…

  ≢⍺ Tally (Länge) von X

  ⍵,/⍨ alle Teilstrings dieser Größe von Y (Lit. Verkettungsreduktion von denen, aber das ist ein No-Op)

  s← store in s(für s ubstrings)

  ⍳⍨¨ɩ Ndex Selfie von jedem von denen

 Jetzt haben wir den Index des ersten Musters oder 1 + die Anzahl der Muster, wenn keine Übereinstimmung gefunden wurde

(... )⊃⍨ benutze diesen Index, um aus ...

  ⊂'No!' die eingeschlossene Zeichenfolge (so dass sie als einzelnes Element fungiert)

  s, vorangestellt mit s

Adam
quelle