Перевірте, чи є ізоморфна підрядок


12

Це питання є розширенням Перевірити, чи слова є ізоморфами і копіює першу частину, щоб дати визначення ізоморфі.

Два слова є ізоморфами, якщо вони мають однаковий зразок повторень літер. Наприклад, обидва ESTATEі DUELEDмають візерунокabcdca

ESTATE
DUELED

abcdca

оскільки букви 1 і 6 однакові, букви 3 і 5 однакові, і більше нічого. Це також означає, що слова пов'язані шифром заміни, тут зі збігом E <-> D, S <-> U, T <-> E, A <-> L.

З огляду на два рядки X і Y, причому X не більше Y, завдання полягає в тому, щоб визначити, чи є підрядник Y, який є ізоморфою з X.

Введення: Два непусті рядки літер з a..zA..Z, по одному рядку на рядок. Це відбуватиметься зі стандартного введення.

Вихідні підрядки другої струни, яка є ізоморфою з першою струною, якщо така річ існує. В іншому випадку виведіть "Ні!".

Правила Ваш код повинен працювати в лінійний час загальною довжиною вводу.

Оцінка Ваш бал - кількість байтів у вашому коді. Виграє найменше байт.


Приклад введення та виведення

adca
ddaddabdaabbcc

dabd

Порада

Існує хоча б одне не таке складне, практично швидке та лінійне рішення часу.


@AlexA. Я думаю, хтось сказав мені, що якщо ви обмежите час роботи / складність, то це повинно бути викликом коду. Я радий змінити це, якщо це, звичайно, неправильно.

7
Якщо час пробігу обмежено правилом і не впливає на забивання, код-гольф краще підходить, ніж виклик коду.
Денніс

лінійний час означає, що він повинен бути O (m + n), а не O (mxn) ні O (mx (nm)), де m, n - довжина першого та другого рядків?
якийсь користувач

@someuser Так, це означає O (m + n).

1
@BetaDecay Див. "З огляду на два рядки X і Y, X не більше Y, завдання - визначити, чи є підрядок Y, який є ізоморфою з X."

Відповіді:


8

Пітон 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 статусом виходу)

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

Алгоритм є варіацією KMP, використовуючи тест на основі індексу для відповідності символів. Основна ідея полягає в тому, що якщо ми отримаємо невідповідність у положенні, X[i]то ми можемо повернутися до наступного можливого місця матчу відповідно до найдовшого суфікса, X[:i]який є ізоморфним префіксу X.

Працюючи зліва направо, ми присвоюємо кожному символу індекс, рівний відстані до останнього попереднього виникнення цього символу, або якщо попереднього виникнення немає, то беремо довжину поточного префікса рядка. Наприклад:

MISSISSIPPI
12313213913

Щоб перевірити, чи збігаються два символи, ми порівнюємо індекси, коригуючи відповідним чином для індексів, більших за довжину поточного (під) рядка.

Алгоритм KMP стає дещо спрощеним, оскільки ми не можемо отримати невідповідність першого символу.

Ця програма виводить перший збіг, якщо такий існує. Я використовую помилку під час виконання для виходу у разі збігу, але код можна легко змінити для чистого виходу ціною деяких байтів.

Примітка. Для обчислення індексів ми можемо використовувати str.rfind(на відміну від мого попереднього підходу із використанням словника) і все ще мати лінійну складність, припускаючи, що str.rfindпочинається пошук з кінця (що здається єдиним розумним вибором реалізації) - для кожного символу в алфавіті , нам ніколи не доведеться двічі проходити одну і ту ж частину рядка, тому є верхня межа порівняння (розмір алфавіту) * (розмір рядка).

Оскільки код досить заплутався в ході гольфу, ось попереднє (293 байт) рішення, яке читати трохи простіше:

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

У eфункціональних тестах еквівалентність символів. Оператор execпризначає індекси та робить деякі змінні ініціалізації. Перший цикл обробляє Xзворотні значення, а другий цикл здійснює пошук рядків.

Оновлення: Ось версія, яка виходить чисто, ціною одного байта:

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

Я думаю, ви можете зберегти байт, виконавши python3. r=raw_inputvs. r = inputзаощаджує 4 байти, а парен на друк коштує лише 3.
Maltysen

@Maltysen дякую за коментар. Я не вважав Python 3, коли писав це, але що стосується вартості та економії, то є додаткові витрати на 2 байти, оскільки ви не можете змішувати пробіли та вкладки для відступу в Python 3.
Мітч Шварц,

@Maltysen просто для подальшого продовження, це питання вже не вкладки, а дужки для exec.
Мітч Шварц

Це дійсно чудова відповідь! Я з нетерпінням чекаю його зараз у cjam :)

6

Пітон 3, 401 байт

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

Це все ще здебільшого невольф, але я думаю, що це має працювати. Основним алгоритмом є KMP , плюс додатковий коефіцієнт, який має фактори за розміром алфавіту (що добре, оскільки алфавіт є постійним). Іншими словами, це / повинен бути один абсолютно непрактичний лінійний алгоритм.

Ось декілька приміток для аналізу:

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

Для тестування ви можете замінити sменший алфавіт, ніж string.ascii_letters.


2

APL (Dyalog) , 32 байти

Це функція інфіксації, приймаючи X як лівий аргумент, а Y як правий аргумент.

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

Спробуйте в Інтернеті!

{} Анонімний лямбда, де і представляють аргументи (X і Y)

⍳⍨⍺ɩ ndex selfie X ( ɩ ndices першого появи елементів X у X)

 додайте, щоб ми могли шукати весь цей візерунок

()⍳Ɩ ndex першого появи цього у…

  ≢⍺ підрахунок (довжина) X

  ⍵,/⍨ всі підрядки такого розміру Y (зменшення конкатенації літ. тих, але це неоперативний варіант)

  s← зберігати в s(для s ubstrings)

  ⍳⍨¨de ndex selfie кожного з них

 тепер у нас є індекс першого шаблону, або 1 + кількість шаблонів, якщо не було знайдено відповідності

()⊃⍨ Використовувати цей індекс для вибору…

  ⊂'No!' додається рядок (щоб він функціонував як єдиний елемент)

  s, претендував на с s

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.