여러 알고리즘이 있지만, 아래 세가지에 대해서 정리할 계획입니다.
이름 그대로 고지식하게 본문 문자열 처음부터 끝까지 차례대로 순회합니다. 일일이 다 비교하면서 동작합니다. 정말 하나하나 비교하므로 특별한 설명을 첨부하지는 않겠습니다.
문자열 각각의 길이를 M
, N
이라 할 때, 시간복잡도는 O(MN)
입니다.
O(M + N)
ABABACA
에 대해서 이를 따져보면 다음과 같습니다.i | 부분 문자열 | 접두사면서 접미사인 최대 문자열 | 길이 |
---|---|---|---|
0 | A | - | 0 |
1 | AB | - | 0 |
2 | ABA | A | 1 |
3 | ABAB | AB | 2 |
4 | ABABA | ABA | 3 |
5 | ABABAC | - | 0 |
6 | ABABACA | A | 1 |
public static int[] failureFunction(String pattern) {
int size = pattern.length();
int[] table = new int[size];
int idx = 0; // 포인터 인덱스
for(int i = 1; i < size; i++ ) { // i : 부분 문자열의 마지막 인덱스
// i가 들어오면서 문자가 하나 추가 된 상황
while(idx > 0 && pattern.charAt(idx) != pattern.charAt(i)) {
idx = table[idx - 1];
}
// 현재 포인터가 새로 추가된 문자와 일치하면 접두사 길이 + 1
// 포인터가 한칸 이동
if(pattern.charAt(idx) == pattern.charAt(i)) {
table[i] = ++idx;
}
}
return table;
}
"ABABACA"에 대해서 배열 만들기
A
idx = 0, i = 0, table[i] = 0
AB
idx = 0, i = 1, table[i] = 0
ABA
idx = 0, i = 2
idx = 1, table[i] = 1
ABAB
idx = 1, i = 3
idx = 2, table[i] = 2
ABABA
idx = 2, i = 4
idx = 3, table[i] = 3
ABABAC
idx = 3, i = 5
idx = 1, i = 5 // ABA에 C가 추가된다면?
idx = 0, i = 5, table[i] = 0
ABABACA
idx = 0, i = 6
idx = 1, table[i] = 1
https://www.acmicpc.net/problem/1786
해당 문제의 소스코드이기도 합니다.
import java.io.*;
public class Main {
static int count;
static StringBuilder sb;
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String t = br.readLine();
String p = br.readLine();
count = 0;
sb = new StringBuilder();
KMP(t, p);
System.out.println(count);
System.out.println(sb);
br.close();
}
public static void KMP(String parent, String pattern) {
int parentSize = parent.length();
int patternSize = pattern.length();
int[] table = failureFunction(pattern);
int patternIdx = 0;
for(int idx = 0; idx < parentSize; idx++) {
while(patternIdx > 0 && parent.charAt(idx) != pattern.charAt(patternIdx)) {
patternIdx = table[patternIdx - 1];
}
if(parent.charAt(idx) == pattern.charAt(patternIdx)) {
if(patternIdx == patternSize - 1) {
count++;
sb.append(idx - patternSize + 2).append(" ");
patternIdx = table[patternIdx]; // 테이블 반드시 확인
// abababab 에서 abab를 찾는 것을 생각해보자.
} else {
patternIdx++;
}
}
}
}
public static int[] failureFunction(String pattern) {
int size = pattern.length();
int[] table = new int[size];
int idx = 0;
for(int i = 1; i < size; i++ ) {
while(idx > 0 && pattern.charAt(idx) != pattern.charAt(i)) {
idx = table[idx - 1];
}
if(pattern.charAt(idx) == pattern.charAt(i)) {
table[i] = ++idx;
}
}
return table;
}
}
O(MN)
이지만 평균적으로는 선형에 가까운 속도를 가지고 있습니다.