Periksa apakah ada substring isomorf

12

Pertanyaan ini merupakan perpanjangan dari Periksa apakah kata-kata adalah isomorf dan menyalin bagian pertama untuk memberikan definisi isomorf.

Dua kata adalah isomorf jika memiliki pola pengulangan huruf yang sama. Misalnya keduanya ESTATEdan DUELEDmemiliki polaabcdca

ESTATE
DUELED

abcdca

karena huruf 1 dan 6 sama, huruf 3 dan 5 sama, dan tidak lebih jauh. Ini juga berarti kata-kata tersebut terkait dengan cipher pengganti, di sini dengan pencocokan E <-> D, S <-> U, T <-> E, A <-> L.

Diberikan dua string X dan Y, dengan X tidak lebih dari Y, tugasnya adalah menentukan apakah ada substring Y yang merupakan isomorf dengan X.

Input: Dua string surat yang tidak kosong dari a..zA..Z yang satu string per baris. Ini akan datang dari input standar.

Output Substring dari string kedua yang merupakan isomorph dengan string pertama, jika hal seperti itu ada. Kalau tidak, output "Tidak!".

Aturan Kode Anda harus berjalan dalam waktu linier dalam total panjang input.

Skor Skor Anda adalah jumlah byte dalam kode Anda. Bytes paling sedikit menang.


Contoh input dan output

adca
ddaddabdaabbcc

dabd

Tip

Setidaknya ada satu solusi waktu yang tidak rumit, praktis cepat dan linear untuk masalah ini.


sumber
@AlexA. Saya pikir seseorang mengatakan kepada saya bahwa jika Anda membatasi waktu berjalan / kompleksitas maka itu harus menjadi tantangan kode. Saya senang mengubahnya jika itu salah tentu saja.
7
Jika run time dibatasi oleh aturan dan tidak mempengaruhi skor, kode-golf lebih cocok daripada kode-tantangan.
Dennis
waktu linear berarti harus O (m + n) dan bukan O (mxn) atau O (mx (nm)) di mana m, n adalah panjang string pertama dan ke-2?
beberapa pengguna
@someuser Ya artinya O (m + n).
1
@ BetaDecay Lihat "Diberikan dua string X dan Y, dengan X tidak lebih dari Y, tugasnya adalah menentukan apakah ada substring Y yang merupakan isomorf dengan X."

Jawaban:

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 dengan 0 status keluar)

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

Algoritma adalah variasi KMP, menggunakan tes berbasis indeks untuk pencocokan karakter. Ide dasarnya adalah bahwa jika kita mendapatkan ketidakcocokan pada posisi X[i]maka kita dapat kembali ke tempat yang memungkinkan untuk pertandingan sesuai dengan sufiks terpanjang X[:i]yaitu isomorfik ke awalan X.

Bekerja dari kiri ke kanan, kami menetapkan setiap karakter indeks yang sama dengan jarak ke kemunculan karakter sebelumnya, atau jika tidak ada kejadian sebelumnya maka kami mengambil panjang awalan string saat ini. Sebagai contoh:

MISSISSIPPI
12313213913

Untuk menguji apakah dua karakter cocok, kami membandingkan indeks, menyesuaikan dengan tepat untuk indeks yang lebih besar dari panjang string (sub) saat ini.

Algoritma KMP menjadi sedikit disederhanakan karena kita tidak bisa mendapatkan ketidakcocokan pada karakter pertama.

Program ini menghasilkan kecocokan pertama jika ada. Saya menggunakan kesalahan runtime untuk keluar jika terjadi kecocokan, tetapi kode dapat dengan mudah dimodifikasi untuk keluar dengan bersih dengan biaya beberapa byte.

Catatan: Untuk menghitung indeks, kita dapat menggunakan str.rfind(sebagai lawan dari pendekatan saya sebelumnya menggunakan kamus) dan masih memiliki kompleksitas linier, dengan asumsi bahwa str.rfindmulai mencari dari akhir (yang tampaknya satu-satunya pilihan implementasi yang waras) - untuk setiap karakter dalam alfabet , kita tidak perlu melewati bagian yang sama dari string dua kali, jadi ada batas atas (ukuran alfabet) * (ukuran string) perbandingan.

Karena kode menjadi agak kabur dalam permainan golf, berikut ini adalah solusi (293 byte) sebelumnya yang sedikit lebih mudah dibaca:

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

The etes fungsi ekivalensi karakter. The execpernyataan memberikan indeks dan melakukan beberapa initialisations variabel. Proses loop pertama Xuntuk nilai jatuh kembali, dan loop kedua melakukan pencarian string.

Pembaruan: Ini adalah versi yang keluar dengan bersih, dengan biaya satu 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
Mitch Schwartz
sumber
Saya pikir Anda dapat menyimpan byte dengan melakukan python3. r=raw_inputvs. r = inputmenghemat 4 byte dan paren pada cetakan hanya biaya 3.
Maltysen
@Maltysen terima kasih atas komentarnya. Saya tidak mempertimbangkan Python 3 saat menulis ini, tetapi untuk biaya dan penghematan, ada biaya 2 byte tambahan karena Anda tidak dapat mencampur ruang dan tab untuk lekukan dalam Python 3.
Mitch Schwartz
@Maltysen hanya untuk menindaklanjuti, masalahnya sekarang bukan lagi tab tetapi tanda kurung exec.
Mitch Schwartz
Ini jawaban yang sangat bagus! Saya berharap untuk melihatnya di cjam sekarang :)
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!")

Ini sebagian besar masih belum diserang, tetapi saya pikir itu harus bekerja. Algoritma inti adalah KMP , ditambah faktor tambahan yang faktorial dalam ukuran alfabet (yang baik-baik saja, karena alfabetnya konstan). Dengan kata lain, ini adalah / harus menjadi salah satu algoritma linear yang sama sekali tidak praktis.

Berikut beberapa penjelasan untuk membantu analisis:

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

Untuk pengujian, Anda dapat mengganti sdengan alfabet yang lebih kecil dari string.ascii_letters.

Sp3000
sumber
2

APL (Dyalog) , 32 byte

Ini adalah fungsi infix, mengambil X sebagai argumen kiri dan Y sebagai argumen kanan.

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

Cobalah online!

{} Lambda anonim di mana dan mewakili argumen (X dan Y)

⍳⍨⍺ɩ ndex selfie X ( ɩ ndices tentang kemunculan pertama elemen X di X)

 lampirkan sehingga kita dapat mencari seluruh pola itu

(... )⍳ɩ ndex kemunculan pertama itu di ...

  ≢⍺ penghitungan (panjang) X

  ⍵,/⍨ semua substring dari ukuran Y (lit. reduksi concatenation dari mereka, tapi itu adalah no-op)

  s← simpan di s(untuk ubstrings s )

  ⍳⍨¨self ndex selfie masing-masing

 sekarang kita memiliki indeks pola pertama, atau 1 + jumlah pola jika tidak ditemukan kecocokan

(... )⊃⍨ gunakan indeks itu untuk memilih dari ...

  ⊂'No!' string terlampir (sehingga berfungsi sebagai elemen tunggal)

  s, diawali dengan s

Adm
sumber