일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | ||||
4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 | 19 | 20 | 21 | 22 | 23 | 24 |
25 | 26 | 27 | 28 | 29 | 30 | 31 |
- 정렬
- Ga
- 유전학알고리즘
- 백준
- 외판원순회
- 카카오테크캠퍼스
- H-모빌리티 클래스
- 코틀린
- 탐색
- 프로그래밍 언어론
- 백준2098
- cpp02
- 데이터과학
- 부동소수점
- 안드로이드스튜디오
- 데이터 과학
- 분할과정복
- 컴퓨터알고리즘
- 배낭문제
- 컴퓨터구조
- 대외활동
- 컴퓨터공학과
- 판다스
- 함수형 프로그래밍
- 그리디
- 알고리즘
- datapath
- 현대자동차
- Today
- Total
minkylee
[String Matching] KMP(Knuth-Morris-Pratt) 알고리즘 본문
서론
String Matching (문자열 매칭) 알고리즘은 주어진 텍스트에서 특정 패턴을 찾을 때 사용하는 알고리즘이다.
대표적인 문자열 매칭 알고리즘에는
- 브루트 포스 알고리즘
- KMP 알고리즘
- Rabin-Karp 알고리즘
- Boyer-Moore 알고리즘
- Sunday 알고리즘
등이 있으며 여기서는 KMP 알고리즘에 대해 알아보려고 한다.
KMP 알고리즘은 Knuth , Morris, Pratt 3명의 사람이 설계한 알고리즘으로, 전체 문자열에서 특정 문자열을 찾는 알고리즘이다.
시간 복잡도는 N(전체 문자열 길이) + M(패턴 문자열 길이) 가 나온다.
결과값으로는 다음과 같이 나올 수 있으며 필요한 값을 반환시키면 된다!
- 인덱스 리스트 : 패턴이 텍스트 내에서 발견된 모든 시작 위치를 반환
- 불리언 값 : 패턴이 텍스트 내에 존재하는 지 여부를 반환
- 첫 매칭 인덱스 : 패턴이 텍스트 내에서 처음으로 발견된 위치를 반환
구현
아이디어
KMP 알고리즘의 가장 중요한 아이디어는 문자열이 불일치 할 때 탐색을 시작했던 위치의 다음 문자 부터가 아닌, 우리가 앞서 탐색했던 정보를 이용하여 몇 칸 정도는 건너 뛴 후 탐색할 수 있지 않을까? 부터 시작한다.
만약 문자열 탐색을 건너 뛸 수 있다면 어떤 전제조건이 꼭 성립해야 할까?
건너 뛴 후의 패턴의 앞 부분과 텍스트의 뒷 부분이 일치해야 한다 가 전제 조건으로 성립해야 한다.
건너 뛴 후의 패턴과 텍스트가 일치해야 그 이후로 탐색을 진행할 것이기 때문이다.
반대로 일정부분 건너 뛴 후 패턴과 텍스트가 일치하지 않는다면, 건너뛰는 의미가 없다.
여기서 얻을 수 있는 결론은 패턴의 앞 부분과 텍스트의 뒷 부분이 동일한 부분까지는 문자열 탐색을 건너뛸 수 있다는 사실이다.
부분 일치 테이블 (LPS 배열) 생성
부분 일치 테이블은 패턴의 각 위치에서 최대한으로 일치하는 접두사(prefix) 와 접미사(suffix) 의 길이를 저장한다.
부분 일치 테이블을 만들기 위해서는 각 위치에서 접두사와 접미사가 일치하는 최대 길이를 기록해야 한다.
패턴 "ABABCABAB" 를 예시로 들어보자
1. 패턴의 첫 글자부터 시작해서 하나씩 진행한다.
2. 첫 번째 글자 'A'
패턴 | A | B | A | B | C | A | B | A | B |
LPS | 0 |
접두사와 접미사가 없으므로 LPS 값은 0이다.
3. 두 번째 글자 'B'
패턴 | A | B | A | B | C | A | B | A | B |
LPS | 0 | 0 |
접두사와 접미사가 없으므로 LPS 값은 0이다.
4. 세 번째 글자 'A'
패턴 | A | B | A | B | C | A | B | A | B |
LPS | 0 | 0 | 1 |
접두사 'A'와 접미사 'A' 가 일치하므로 LPS 값은 1이다.
5. 네 번째 글자 'B'
패턴 | A | B | A | B | C | A | B | A | B |
LPS | 0 | 0 | 1 | 2 |
접두사 'AB'와 접미사 'AB'가 일치하므로 LPS 값은 2이다.
6. 다섯 번째 글자 'C'
패턴 | A | B | A | B | C | A | B | A | B |
LPS | 0 | 0 | 1 | 2 | 0 |
접두사와 접미사가 일치하지 않으므로 LPS 값은 0이다.
이런식으로 LPS 테이블을 채우면
패턴 | A | B | A | B | C | A | B | A | B |
LPS | 0 | 0 | 1 | 2 | 0 | 1 | 2 | 3 | 4 |
이런 배열을 얻을 수 있다.
이를 통해 패턴이 일치하지 않는 경우에도 이미 일치한 부분을 활용하여 불필요한 비교를 줄일 수 있다.
패턴과 텍스트 비교
텍스트의 각 위치에서 패턴을 비교하되, 일치하지 않는 경우 LPS 테이블을 활용해 불필요한 비교를 건너뛴다.
예를 들어, 텍스트(T)가 ABABDABACDABABCABAB 이고 패턴(P)이 ABABCABAB 일 때, 다음과 같은 과정으로 비교한다.
- 텍스트의 처음부터 패턴을 비교하기 시작한다.
- 일치하지 않는 위치에서, 패턴의 일치된 부분을 이용해 LPS 테이블을 참조한다.
- 얻은 값만큼 패턴을 뒤로 밀어준다.
패턴의 P[3]까지는 일치하지만 P[4]부터 불일치가 발생한다.
현재 탐색하고 있는 문자의 위치는 4, 여기서 LPS[3] 을 빼주면 KMP 알고리즘의 아이디어대로 접두사, 접미사가 일치하는 다음 탐색 위치를 한 번에 얻을 수 있게 된다.
그러므로 4 - 2 = 2 만큼 패턴을 뒤로 밀어준다.
패턴의 P[1]까지는 일치하지만 P[2]부터 불일치가 발생한다.
현재 탐색하고 있는 문자의 위치는 4, 여기서 P[1]를 빼주면 4 - 0 = 4를 다음 탐색 위치로 지정한다.
일치하는 부분이 없을 경우에는 한 칸 뒤로 밀어준다. 다음 탐색위치는 5가 된다.
이번에는 텍스트의 8번째 문자, P[3]에서 불일치가 발생했다.
8 - 1 = 7을 다음 탐색 위치로 지정한다.
이런식으로
현재 탐색하고 있는 문자의 위치 - 마지막으로 일치한 패턴의 LPS 값 = 다음 탐색 시작 위치
식을 사용하여 일치하는 문자열을 찾아가면 된다.
이렇게 8번만에 일치 문자열을 찾았다. 가장 끝에 있어 브루트포스 방법의 경우 11개의 문자열 비교를 했어야 했는데 KMP를 이용하여 비교 횟수를 현저하게 줄일 수 있었다.
이는 문자열을 한 번만 읽고 탐색하는 것과 같은 시간복잡도 이므로 이론상으로 가장 빠른 문자열 탐색 알고리즘이다.
https://cano721.tistory.com/21
[알고리즘 개념] KMP - 문자열 검색 알고리즘(JAVA 코드)
KMP란 문자열 검색 알고리즘. Knuth-Morris-Pratt 3명의 사람이 설계한 알고리즘으로, 전체 문자열에서 특정 문자열을 찾는 알고리즘. 시간복잡도: N+M N(전체 문자열 길이), M(패턴 문자열 길이) 브루트
cano721.tistory.com
https://injae-kim.github.io/dev/2020/07/23/all-about-kmp-algorithm.html
'CSE > 컴퓨터알고리즘' 카테고리의 다른 글
유전학 알고리즘 (Genetic Algorithm) MAXONE example (1) | 2024.06.10 |
---|---|
[최단거리] A* 알고리즘 (0) | 2024.05.09 |
[Greedy] 거스름돈 문제 (0) | 2024.04.22 |
[분할과 정복] Strassen algorithm (1) | 2024.04.21 |
[분할과 정복] Closest Pair Problem (0) | 2024.04.21 |