Knuth-Morris-Pratt Algorithm, KMP Algorithm
KMP 알고리즘을 학습하기에 앞서
i |
0 |
1 |
2 |
3 |
4 |
5 |
6 |
7 |
N[i] |
a |
aa |
aab |
aaba |
aabaa |
aabaab |
aabaaba |
aabaabac |
str |
- |
a |
- |
a |
aa |
aab |
aaba |
- |
f[i] |
0 |
1 |
0 |
1 |
2 |
3 |
4 |
0 |
KMP 알고리즘의 시간 복잡도
패턴 arr에서 실패함수(f[i])를 구하는 부분과 이를 이용하여 전체 문자열에서 arr를 찾는 부분으로 나누어 생각할 수 있다.
실패함수를 찾는데 걸리는 시간 O(m), 전체 문자열에서 패턴 arr을 찾는 시간 O(n)이므로 총 O(n+m)의 시간이 소모된다.
KMP 알고리즘의 이용 예시
위의 표를 이용하여 KMP 알고리즘을 어떻게 쓸 수 있는지 알아보자.
이 내용을 집중하여 잘 본다면 KMP 알고리즘에 대해 잘 이해 할 수 있을 것이다.
aabaabaaaabaabaadaabaabac가 있다고 가정하자.
여기서 aabaabac를 찾으려 한다면 위의 표를 다음과 같이 이용하면 된다.
aabaabaaaabaabaadaabaabac
aabaabac
비교결과 :: aabaaba까지 같다. 따라서 arr[i]의 7번째 인덱스에서 a와 c가 틀렸으니,
aabaaba의 f[i] = 4 따라서 (7-4) = 3인 arr[3]부터 다시 확인한다.
aabaabaaaabaabaadaabaabac
aabaabac
비교결과 :: aabaa까지 같다. 따라서 arr[i]의 8번째 인덱스에서 a와 b가 틀렸으니,
aabaa의 f[i] = 2 따라서 (8-2) = 6인 arr[6]부터 다시 확인한다.
aabaabaaaabaabaadaabaabac
aabaabac
aabaabaaaabaabaadaabaabac
aabaabac
비교결과 :: aa까지 같다. 따라서 arr[i]의 9번째 인덱스에서 a와 b가 틀렸으니,
aabaabaaaabaabaadaabaabac
aabaabac
비교결과 :: aabaaba까지 같다. 따라서 arr[i]의 15번째 인덱스에서 a와 c가 틀렸으니,
aabaabaaaabaabaadaabaabac
aabaabac
비교결과 :: aabaa까지 같다. 따라서 arr[i]의 16번째 인덱스에서 d와 b가 틀렸으니,
aabaabaaaabaabaadaabaabac
aabaabac
비교결과 :: aa까지 같다. 따라서 arr[i]의 16번째 인덱스에서 d와 b가 틀렸으니,
aabaabaaaabaabaadaabaabac
aabaabac
비교결과 :: a까지 같다. 따라서 arr[i]의 16번째 인덱스에서 d와 a가 틀렸으니,
aabaabaaaabaabaadaabaabac
aabaabac
비교결과 :: 같은 것이 없다. 따라서 arr[17]부터 다시 확인한다.
aabaabaaaabaabaadaabaabac
aabaabac
비교결과 :: 모두 일치. 정답을 도출한다.( 일치하는 문자열의 시작 인덱스 :: 17, 일차하는 문자열의 개수 :: 1)
아래는 KMP 알고리즘에 대한 소스 코드이며, 여러 문제는 다음 링크를 통해 확인이 가능하다.
http://www.crocus.co.kr/search/kmp
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 32 33 34 35 36 37 38 39 40 | vector<int> getPi(string p) { int m = (int)p.size(); int j = 0; vector<int> pi(m, 0); for (int i = 1; i< m; i++) { while (j > 0 && p[i] != p[j]) j = pi[j - 1]; if (p[i] == p[j]) pi[i] = ++j; } return pi; } vector<int> kmp(string s, string p) { vector<int> ans; auto pi = getPi(p); int n = (int)s.size(), m = (int)p.size(), j = 0; for (int i = 0; i < n; i++) { while (j>0 && s[i] != p[j]) j = pi[j - 1]; if (s[i] == p[j]) { if (j == m - 1) { ans.push_back(i - m + 1); j = pi[j]; } else { j++; } } } return ans; } // This source code Copyright belongs to Crocus // If you want to see more? click here >> | Crocus |
'Applied > 알고리즘' 카테고리의 다른 글
에라토스테네스의 체(Eratosthenes' sieve) (0) | 2017.01.09 |
---|---|
전위, 후위 순회를 알 때 트리 구하는 알고리즘 (0) | 2016.12.07 |
우선순위 큐 다익스트라 알고리즘(Dijkstra Algorithm) (5) | 2016.11.21 |
플로이드 워셜 알고리즘(Floyd Warshall Algorithm) 소스 코드 (2) | 2016.11.17 |
플로이드 워셜 알고리즘(Floyd Warshall Algorithm) 개념 (12) | 2016.11.17 |