[알고리즘] 스트링 알고리즘
스트링 알고리즘
- 스트링(string) : 문자가 연속적으로 나열된 문자열 (ex. ATATCGCCCACGTAT)
- 알파벳(∑) : 스트링에 사용되는 문자들의 집합 (ex. ∑={A,C,G,T})
- 스트링 매칭, 스트링 압축 등의 문제를 해결하는 알고리즘
스트링 매칭
- 텍스트에서 패턴이 나타나는 위치를 찾는 것
- 텍스트의 길이 n은 패턴의 길이 m보다 크거나 같다.
브루트-포스 스트링 매칭알고리즘
- 텍스트의 각 위치에서부터 패턴의 길이만큼 문자를 비교하며 매치를 찾는 방법
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
const T = "AABAABAABAA";
const P = "AABAA";
const checkMatchIndex = (T, P) => {
let result = [];
for (let i = 0; i <= T.length - P.length; i++) {
for (let j = 0; j < P.length; j++) {
if (T[i + j] !== P[j]) break;
if (j === P.length - 1) {
result.push(i);
}
}
}
return result;
};
console.log(checkMatchIndex(T, P));
// [0, 3, 6]
시간복잡도
- O(nm)
라빈-카프 알고리즘
- 패턴의 해시값으로 매치의 후보를 찾고, 후보에 대해서만 문자별로 비교해서 매치를 찾는 방법
- 직전 위치의 해시값을 이용하여 상수 시간에 계산 가능
시간 복잡도
- O(n+km) (k: 매치의 개수)
- 전처리 : O(m)
- 텍스트에서 해시값 계산 : O(n)
- 후보 위치는 문자 직접 비교, 매치 개수 k : O(km)
- 최선 : 매치 개수가 상수라면 O(n)
- 최악 : 모든 자리에서 매치가 발생하면 O(nm)이다.
KMP 알고리즘
- 패턴 내의 문자들의 관계를 이용하여 매칭 시 중복된 비교를 줄임
- 매치되는 위치를 찾은 후 패턴의 앞부분(접두부)과 뒷부분(접미부)이 최대로 일치하는 서브스트링의 접두부의 끝 문자 위치를 다음에 비교할 위치로 적용한다.
- 텍스트 T=’aabaabaaa’, 패턴 P=’aabaa’ 일 때, 위치 0에서 텍스트와 패턴의 문자를 비교하여 매치를 찾게 된다.
- 패턴의 접두부와 접미부가 최대로 일치하는 서브스트링이 ‘aa’이므로 위치 4에 접두부의 끝 문자의 위치인 1이 위치하도록 적용하여 위치 5부터 ‘baa’를 비교하면 된다. (f₄=1)
- 접두부와 접미부의 최대 일치가 없으면 -1이다. 이 때는 패턴을 적용한 위치를 건너뛰고 다음 위치부터 비교한다.
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
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
// 접두부와 접미부의 최대 일치 서브스트링을 찾는 전처리 알고리즘
function preKMP(pattern) {
const lps = new Array(pattern.length).fill(0);
let length = 0;
let i = 1;
while (i < pattern.length) {
if (pattern[i] === pattern[length]) {
length++;
lps[i] = length;
i++;
} else {
if (length !== 0) {
length = lps[length - 1];
} else {
lps[i] = 0;
i++;
}
}
}
return lps;
}
// KMP 알고리즘
function KMPSearch(text, pattern) {
const lps = preKMP(pattern);
const result = [];
let i = 0; // text의 인덱스
let j = 0; // pattern의 인덱스
while (i < text.length) {
if (pattern[j] === text[i]) {
i++;
j++;
}
if (j === pattern.length) {
result.push(i - j);
j = lps[j - 1];
} else if (i < text.length && pattern[j] !== text[i]) {
if (j !== 0) {
j = lps[j - 1];
} else {
i++;
}
}
}
return result;
}
// 예제 사용
const text = "aacaabaabaa";
const pattern = "aabaa";
const result = KMPSearch(text, pattern);
시간복잡도
- 전처리 : O(m)
- 매칭 : O(n)
- n≥m 이므로 전체 성능은 O(n)
보이어-무어 알고리즘
- 패턴 내의 문자들의 관계를 이용하여 매칭 시 중복된 비교를 줄임
- 텍스트의 첫 위치에서 패턴의 뒷부분부터 문자 비교
- 불일치 또는 매치를 발견하면 불일치 문자 방법과 일치 접미부 방법 중 더 많이 이동시킬 수 있는 값 선택
시간복잡도
- 전처리 : O(m)
- 최선 : O(n/m)
- 최악 : O(nm)
스트링 압축
RLE
- 스트링에서 연속으로 나타나는 문자와 반복 횟수로 압축하는 방법
- 인코딩 예 → aaaaabb → (a,5) (b,2)
- 디코딩 예 → (a,5) (b,2) → aaaaabb
성능
- O(n)
This post is licensed under CC BY 4.0 by the author.