본문 바로가기

PS - 알고리즘

KMP

 

어떤 문자열(이하 text)에서 특정 문자열(이하 pattern)이 있는지 판단하는 문제가 있다 합시다(https://www.acmicpc.net/problem/1786). text의 길이를 $N$, pattern의 길이를 $M$이라 하면 단순 비교로 할 때 $O(NM)$의 시간이 걸립니다. 이를 $O(N+M)$으로 만들어 주는 것이 KMP(Knuth-Morris-Pratt Algorithm)입니다.

방법

단순 비교로 하면 $O(NM)$이 걸린다고 했었죠. KMP는 이 알고리즘에서 불필요한 과정을 제거했습니다.

 

위 과정을 보면 aaaaaa 부분은 더이상 안봐도 될 것 같은데 계속해서 검사해 주고 있습니다. 이 부분을 줄이기 위해서는 fail function에 대해 알아야 합니다.

fail function

fail function(F)은 dp값이라고 생각하면 됩니다. 정확한 정의는 pattern의 각 위치에서 실패했을 때 다음 비교를 어디에서부터 해야 하는지 저장한 배열이지만, 이 글에서는

 

해당 위치까지의 부분 문자열이 갖는 접두사와 접미사가 일치하는 최대 길이

 

로 하겠습니다. 아래는 12131214와 aaaaaab의 F입니다. 각 pattern 밑에 있는 것이 F입니다.

F는 dp를 이용해 구할 수 있습니다. F[i-1]번째 값을 구했다고 해 봅시다. F[i]는 F[i-1] + 1 보다 클 수 없습니다(만약 F[i]=x라면 F[i-1]=x-1입니다). 만약 pattern[F[i-1]] = pattern[i]라면 F[i]는 F[i-1] + 1입니다.

pattern[F[i-1]] != pattern[i]라고 해 봅시다. F[i-1]는 i-1에서 접두사와 접미사가 일치하는 최대 길이입니다. 그럼 두번째 길이는 얼마일까요? 바로 F[F[i-1]]입니다(정확히는 F[F[i-1]-1]). 세번째 길이는 F[F[F[i-1]]]이겠네요(정확히는 F[F[F[i-1]-1]-1]).

그럼 pattern[F[F[i-1]-1]] = pattern[i]면 F[F[i-1]-1]+1, pattern[ F[F[F[i-1]-1]-1] ] = pattern[i]면 F[F[F[i-1]-1]-1]+1, 이런 식으로 진행하면서 값을 채워주면 됩니다.

그래서 어떻게 하나요?

F는 구했는데 이것으로 어떻게 text와 일치하는 부분을 찾을 수 있을까요?

F의 원래 정의는 "실패했을 때 다음 비교를 어디에서부터 해야 하는지 저장한 배열"입니다. 매칭을 쭉 진행하다 중간에 틀렸다고 해봅시다.

이때, 1213121의 F값은 3입니다. 즉, 처음부터 볼 필요 없이 3번째부터 진행해도 된다는 뜻입니다.

시간복잡도

현재 비교하고 있는 pattern의 위치를 $i$, text의 위치를 $j$라고 합시다. 만약 두개가 일치하면 $i$, $j$ 모두 1 증가합니다. 일치하지 않으면 $i$가 적어도 1 감소합니다. 즉, $O(N+M)$에 해결이 가능합니다.

 

KMP의 핵심은 F입니다. 접두사와 접미사가 일치한다는 것에 집중하면 이 글에서 빠트린 부분도 충분히 직접 유도할 수 있습니다. 이 글에 부족한 부분을 알려주시면 바로 보충하도록 하겠습니다.

'PS - 알고리즘' 카테고리의 다른 글

4544 - 'Roid Rage  (0) 2024.08.01
27933 - 대회 이름 정하기  (0) 2024.07.25
6496 - Cyclic antimonotonic permutations  (0) 2024.07.18
16570 - 앞뒤가 맞는 수열  (0) 2024.07.11
10523 - 직선 찾기  (0) 2024.07.11