동형 하위 문자열이 있는지 확인


12

이 질문은 단어가 동형인지 확인 하고 첫 번째 부분을 복사하여 동형 의 정의를 제공 하는 확장입니다 .

두 단어가 동일한 문자 반복 패턴을 갖는 경우 이소 형입니다. 예를 들어, 모두 ESTATEDUELED패턴을 가지고abcdca

ESTATE
DUELED

abcdca

글자 1과 6이 같기 때문에 글자 3과 5는 같고 더 이상 없습니다. 이것은 또한 단어가 대체 암호와 관련이 있으며 여기서 일치하는을 의미 E <-> D, S <-> U, T <-> E, A <-> L합니다.

X가 Y보다 길지 않은 두 개의 스트링 X와 Y가 주어지면, 작업은 X와 동형 인 Y의 서브 스트링이 있는지 확인하는 것입니다.

입력 : a..zA..Z에서 비어 있지 않은 두 개의 문자열로 한 줄에 한 문자열입니다. 이것은 표준 입력에서 나옵니다.

출력 첫 번째 문자열이있는 경우 두 번째 문자열의 하위 문자열 인 첫 번째 문자열의 하위 문자열입니다. 그렇지 않으면 "No!"를 출력하십시오.

규칙 코드는 입력의 전체 길이에서 선형 시간으로 실행되어야합니다.

점수 점수는 코드의 바이트 수입니다. 가장 적은 바이트가 이깁니다.


입력 및 출력 예

adca
ddaddabdaabbcc

dabd

이 문제에 대해 그렇게 복잡하고 실질적으로 빠르고 선형적인 시간 솔루션이 적어도 하나는 없습니다 .


@AlexA. 누군가가 당신이 실행 시간 / 복잡성을 제한하면 코드 도전이어야한다고 나에게 말했다. 물론 그것이 틀렸다면 그것을 바꾸게되어 기쁩니다.

7
런타임이 규칙에 의해 제한되고 스코어링에 영향을 미치지 않는 경우 코드 골프는 코드 도전보다 더 적합합니다.
Dennis

선형 시간은 O (m + n)이어야하고 O (mxn) 또는 O (mx (nm))가 아니어야합니다. 여기서 m, n은 첫 번째와 두 번째 문자열의 길이입니까?
일부 사용자

@someuser 예, O (m + n)을 의미합니다.

1
@BetaDecay "두 개의 문자열 X와 Y를 제공합니다. X는 Y보다 길지 않으며 X의 동형 인 Y의 하위 문자열이 있는지 확인하는 것입니다."

답변:


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는 폴백 값을 처리 하고 두 번째 루프는 문자열 검색을 수행합니다.

업데이트 : 다음은 1 바이트의 비용으로 깨끗하게 종료되는 버전입니다.

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

의견을 보내 주셔서 감사합니다. 이 글을 쓰는 경우 파이썬 3을 고려하지 않았지만, 비용 절감, 당신은 파이썬 3에서 들여 쓰기 공백과 탭을 혼합 할 수 있기 때문에 추가로 2 바이트 비용이
미치 슈워츠

@Maltysen은 후속 조치로 문제가 더 이상 탭이 아니라 괄호입니다 exec.
Mitch Schwartz

이것은 정말 좋은 답변입니다! 나는 지금 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 셀카 X의 ( ɩ의 X X의 요소가 최초로 출현 ndices)

 우리는 전체 패턴을 찾을 수 있도록 동봉

()⍳that ndex 의 첫 번째 발생…

  ≢⍺ X의 탈리 (길이)

  ⍵,/⍨ 그 크기의 Y의 모든 하위 문자열

  s← 에 저장 s(대한 의의 ubstrings)

  ⍳⍨¨각 of ndex 셀카

 이제 첫 번째 패턴의 색인이 있거나 일치하는 항목이 없으면 1 + 패턴 수

()⊃⍨ 그 색인을 사용하여 선택하십시오…

  ⊂'No!' 동봉 된 문자열 (단일 요소로 기능하도록)

  s, 접두사 s

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.