Controlla se c'è una sottostringa isomorfa


12

Questa domanda è un'estensione di Controlla se le parole sono isomorfi e ne copia la prima parte per dare la definizione di un isomorfo.

Due parole sono isomorfi se hanno lo stesso modello di ripetizioni di lettere. Ad esempio, entrambi ESTATEe DUELEDhanno un modelloabcdca

ESTATE
DUELED

abcdca

poiché le lettere 1 e 6 sono uguali, le lettere 3 e 5 sono uguali e nient'altro. Questo significa anche che le parole sono correlate da un codice di sostituzione, qui con la corrispondenza E <-> D, S <-> U, T <-> E, A <-> L.

Date due stringhe X e Y, con X non più lunga di Y, il compito è determinare se esiste una sottostringa di Y che è un isomorfo con X.

Input: due stringhe di lettere non vuote da a..zA..Z di cui una stringa per riga. Questo verrà dall'input standard.

Output Una sottostringa della seconda stringa che è un isomorfo con la prima stringa, se esiste una cosa del genere. Altrimenti, emettere "No!".

Regole Il codice deve essere eseguito in tempo lineare per la lunghezza totale dell'input.

Punteggio Il tuo punteggio è il numero di byte nel tuo codice. Vince il minor numero di byte.


Esempio di input e output

adca
ddaddabdaabbcc

dabd

Mancia

C'è almeno non che complicato, praticamente veloce e soluzione di tempo lineare a questo problema.


@AlexA. Penso che qualcuno mi abbia detto che se limiti il ​​tempo di esecuzione / complessità, dovrebbe essere una sfida al codice. Sono felice di cambiarlo se questo è ovviamente sbagliato.

7
Se il tempo di esecuzione è limitato da una regola e non influenza il punteggio, il code-golf si adatta meglio del code-challenge.
Dennis,

tempo lineare significa che deve essere O (m + n) e non O (mxn) né O (mx (nm)) dove m, n sono la lunghezza della prima e della seconda stringa?
alcuni utenti il

@someuser Sì significa O (m + n).

1
@BetaDecay Vedi "Dati due stringhe X e Y, con X non più lunga di Y, il compito è determinare se esiste una sottostringa di Y che è un isomorfo con X".

Risposte:


8

Python 2, 338 326 323 321 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 2222 221 220 218 con 0 stato di uscita)

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!'

L'algoritmo è una variante di KMP, che utilizza un test basato sull'indice per la corrispondenza dei caratteri. L'idea di base è che se otteniamo una discrepanza nella posizione, X[i]possiamo tornare al prossimo posto possibile per una partita in base al suffisso più lungo X[:i]che è isomorfo a un prefisso di X.

Operando da sinistra a destra, assegniamo a ciascun carattere un indice pari alla distanza dall'occorrenza precedente più recente di quel carattere, oppure se non si verifica un'occorrenza precedente prendiamo la lunghezza del prefisso di stringa corrente. Per esempio:

MISSISSIPPI
12313213913

Per verificare se due caratteri corrispondono, confrontiamo gli indici, regolandoli in modo appropriato per gli indici che sono maggiori della lunghezza della (sotto) stringa corrente.

L'algoritmo KMP diventa un po 'semplificato poiché non è possibile ottenere una mancata corrispondenza con il primo carattere.

Questo programma genera la prima corrispondenza, se esiste. Uso un errore di runtime per uscire in caso di corrispondenza, ma il codice può essere facilmente modificato per uscire in modo pulito al costo di alcuni byte.

Nota: per gli indici di calcolo, possiamo usare str.rfind(al contrario del mio approccio precedente usando un dizionario) e avere comunque una complessità lineare, supponendo che str.rfindinizi la ricerca dalla fine (che sembra l'unica scelta di implementazione sensata) - per ogni carattere dell'alfabeto , non dobbiamo mai attraversare due volte la stessa parte della stringa, quindi esiste un limite superiore di confronti (dimensione dell'alfabeto) * (dimensione della stringa).

Poiché il codice è stato abbastanza offuscato nel corso del golf, ecco una soluzione precedente (293 byte) che è un po 'più facile da leggere:

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!'

La efunzione verifica l'equivalenza dei caratteri. L' execistruzione assegna indici e fa alcune inizializzazioni variabili. Il primo ciclo elabora i Xvalori di fallback e il secondo ciclo esegue la ricerca di stringhe.

Aggiornamento: ecco una versione che esce in modo pulito, al costo di un byte:

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

Penso che puoi salvare un byte facendo python3. r=raw_inputvs. r = inputsalva 4 byte e le parentesi sulla stampa costano solo 3.
Maltysen,

@Maltysen grazie per il commento. Non ho preso in considerazione Python 3 durante la stesura di questo, ma per quanto riguarda costi e risparmi, c'è un costo aggiuntivo di 2 byte poiché non è possibile mescolare spazi e tab per il rientro in Python 3.
Mitch Schwartz

@Maltysen solo per dare seguito, il problema non è più tabulazioni ma parentesi exec.
Mitch Schwartz,

Questa è davvero un'ottima risposta! Non vedo l'ora di vederlo in cjam ora :)

6

Python 3, 401 byte

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!")

Questo è ancora per lo più non golfato, ma penso che dovrebbe funzionare. L'algoritmo di base è KMP , oltre a un fattore aggiuntivo che è fattoriale nella dimensione dell'alfabeto (il che va bene, poiché l'alfabeto è costante). In altre parole, questo è / dovrebbe essere un algoritmo lineare completamente impraticabile.

Ecco alcune annotazioni per aiutare con l'analisi:

# 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!")

Per i test, è possibile sostituire scon un alfabeto più piccolo di string.ascii_letters.


2

APL (Dyalog) , 32 byte

Questa è una funzione infix, prendendo X come argomento di sinistra e Y come argomento di destra.

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

Provalo online!

{... } lambda anonimo dove e rappresentano gli argomenti (X e Y)

⍳⍨⍺ɩ NDICE selfie di X ( ɩ ndices della prima occorrenza di elementi di X in X)

 allegare in modo che possiamo cercare l'intero modello

(... )⍳ɩ ndex della prima occorrenza di quello in ...

  ≢⍺ riscontro (lunghezza) di X

  ⍵,/⍨ tutte le sottostringhe di quella dimensione di Y (lett. riduzione della concatenazione di quelle, ma questa è una no-op)

  s← negozio a s(per i ubstrings)

  ⍳⍨¨ɩ NDICE selfie di ciascuno di questi

 ora abbiamo l'indice del primo modello o 1 + il numero di modelli se non viene trovata alcuna corrispondenza

(... )⊃⍨ usa quell'indice per scegliere da ...

  ⊂'No!' la stringa racchiusa (in modo che funzioni come un singolo elemento)

  s, anteposto a s

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.