노션 정리본 : https://mint-truck-226.notion.site/KMP-6f2b91cc1f1144fba8834cdf1299addd
⇒ 즉, 특정 패턴이 주어졌을 때 전체 문자열을 빠르게 검색하여 그 패턴이 어디에 등장하는지 찾아주는 알고리즘
시간복잡도 : O(n+m) > 완전 탐색은 O(nm)이다..
1) 주어진 패턴에 대해
skip
배열을 만든다.
skip
은 패턴의 각 인덱스마다 접두사와 접미사가 동일한 갯수2) skip 배열을 활용하여 패턴의 등장 위치를 찾는다. (빠르게)
※ 먼저 알아야 하는 것!
자, 드가자-!
주어진 퀴즈는 다음과 같았다.
kmp_match.py 코드를 실행할 때,
s1 = "abcabgabcaabcabcabgabcaef"
s2 = "abcabgabcae"
가 입력으로 주어지면
skip 배열과 count 값이 어떻게 될 지 맞춰 보시면 됩니다.
kmp_match.py는 책에 있는 걸 그대로 옮겨 오되, print 문만 추가했습니다.
1) 패턴 분석 : 주어진 패턴에 대해 skip 배열을 만든다. → O(n)
※ 잠깐! 왜 접두사 접미사가 같은지를 굳이..알아야 할까?
: 동일성이 보장되는 구간을 찾음으로써 불필요한 반복을 줄일 수 있다!
즉, 25번째 문자까지 같았는데, 26번째에..! 하나 틀렸다고 다시 1번째로 돌아가지 않고 → 25번째 문자까지 접두==접미 같았던 부분이 몇 개였는지 파악해서, 접두+1 위치부터 탐색하는 게 더 빠르고 낭비도 덜하다!
def kmp_match(txt: str, pat: str) -> int:
pt = 1
pp = 0
skip = [0] * (len(pat) + 1)
skip[pt] = 0
while pt != len(pat):
if pat[pt] == pat[pp]:
pt += 1
pp += 1
skip[pt] = pp
elif pp == 0:
pt += 1
skip[pt] = pp
else:
pp = skip[pp]
... (이하 생략)
< 필요한 것 >
skip
배열의 1번째 값은 무조건 0이다. (한 글자짜리는 접두/접미사 X)pt
와 pp
가 사용된다.< 동작 >
주어진 패턴 pat
에 대해, [pt]와 [pp]를 비교한다. (패턴 끝까지)
▶ “패턴 pat에서 n번째 위치까지는 접두사와 접미사가 동일한 부분이 몇 개이다”를 파악해서, 다음 파트에서 활용할 거다!
2) 패턴 / 문자열 비교 : skip 배열을 활용하여 패턴의 등장 위치를 찾는다. (빠르게) → O(m)
pt = pp = 0
count = 0
while pt != len(txt) and pp != len(pat):
if txt[pt] == pat[pp]:
pt += 1
pp += 1
elif pp == 0:
pt += 1
else:
pp = skip[pp]
count += 1
print("count: " + str(count))
return pt - pp if pp == len(pat) else -1
< 필요한 것 >
pt
와 pp
→ 모두 0으로 초기화!count
: 아깝..? (앞 부분은 동일하다가, 뜬금 없이 틀린 것) → 점프!!! (필수적인 건 아니다.)< 동작 >
주어진 문자열 txt
내에서, 패턴 pat
을 찾는다. (비교한다)
KMP..이해하는 데에 하루 걸리고, pp = skip[pp]
이 부분 이해하는 데에 하루 더 걸린 것 같다. 왜 악명 높다고 하는지 알 것 같다.
그래도 이해해 가는 과정이 즐거웠으니 됐다-! (까먹지만 않기를..)