Z 알고리즘?
Z 알고리즘은 문자열에서 특정 패턴을 찾거나 문자열 내에서 일치하는 부분을 빠르게 계산하는 효율적인 방법입니다. 주어진 문자열에서 각 접두사와 원래 문자열의 접두사와의 일치하는 길이를 저장하는 Z 배열
을 기반으로 동작합니다.
Z 배열이란?
- Z 배열의 각 요소는 해당 인덱스에서 시작하는 부분 문자열이 원래 문자열의 처음부터 얼마나 긴 접두사와 일치하는지를 나타냅니다.
- 예를 들어, 문자열
S = "abcababc"
에 대한 Z 배열은[8, 0, 0, 2, 0, 0, 3, 0]
입니다. 첫 번째 인덱스는 문자열 전체와 일치하므로8
, 네 번째 인덱스부터abc
와 일치하는 접두사 길이가2
임을 보여줍니다.
사용 사례
- 문자열 검색: 긴 텍스트에서 짧은 패턴을 찾는 데 사용됩니다. KMP 알고리즘과 유사하지만, Z 알고리즘은 패턴을 별도로 전처리하지 않고, 패턴과 텍스트를 한 문자열로 합쳐 처리하는 방식으로 동작합니다.
- 생물 정보학: DNA 서열에서 특정 서열 패턴을 검색하는 데 사용됩니다.
- 검색 엔진: 패턴이 포함된 문서 검색에도 사용됩니다.
JavaScript 예제 코드: Z 알고리즘
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
function buildZArray(s) {
const n = s.length;
const Z = Array(n).fill(0);
let l = 0,
r = 0,
k;
for (let i = 1; i < n; i++) {
if (i > r) {
l = r = i;
while (r < n && s[r] === s[r - l]) {
r++;
}
Z[i] = r - l;
r--;
} else {
k = i - l;
if (Z[k] < r - i + 1) {
Z[i] = Z[k];
} else {
l = i;
while (r < n && s[r] === s[r - l]) {
r++;
}
Z[i] = r - l;
r--;
}
}
}
return Z;
}
function zSearch(text, pattern) {
const concat = pattern + "$" + text;
const Z = buildZArray(concat);
const m = pattern.length;
for (let i = 0; i < Z.length; i++) {
if (Z[i] === m) {
console.log("Pattern found at index", i - m - 1);
}
}
}
// 예제 사용
const text = "abcabcababcabc";
const pattern = "abc";
zSearch(text, pattern); // Output: Pattern found at index 0, 3, 6, 9
설명:
- Z 배열 생성: 주어진 문자열에 대해 Z 배열을 계산하여 각 인덱스에서 시작하는 부분 문자열이 원본 문자열의 접두사와 얼마나 일치하는지 계산합니다.
- 패턴 검색: 패턴과 텍스트를 합쳐서 처리한 뒤, Z 배열에서 패턴의 길이와 일치하는 값을 찾으면 해당 패턴이 텍스트 내에 존재함을 알 수 있습니다.
Rust 예제 코드: Z 알고리즘
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
fn build_z_array(s: &str) -> Vec<usize> {
let n = s.len();
let mut z = vec![0; n];
let s_bytes = s.as_bytes();
let (mut l, mut r, mut k);
for i in 1..n {
if i > r {
l = i;
r = i;
while r < n && s_bytes[r] == s_bytes[r - l] {
r += 1;
}
z[i] = r - l;
r -= 1;
} else {
k = i - l;
if z[k] < r - i + 1 {
z[i] = z[k];
} else {
l = i;
while r < n && s_bytes[r] == s_bytes[r - l] {
r += 1;
}
z[i] = r - l;
r -= 1;
}
}
}
z
}
fn z_search(text: &str, pattern: &str) {
let concat = format!("{}${}", pattern, text);
let z = build_z_array(&concat);
let m = pattern.len();
for i in 0..z.len() {
if z[i] == m {
println!("Pattern found at index {}", i - m - 1);
}
}
}
fn main() {
let text = "abcabcababcabc";
let pattern = "abc";
z_search(text, pattern); // Output: Pattern found at index 0, 3, 6, 9
}
설명:
- Z 배열 생성: Rust에서는 문자열을 바이트 배열로 변환하여 각 인덱스에서 부분 문자열이 원본 문자열의 접두사와 얼마나 일치하는지 계산합니다.
- 패턴 검색: 패턴과 텍스트를 결합한 후, Z 배열을 생성하여 일치하는 패턴의 위치를 찾아 출력합니다.
요약:
Z 알고리즘은 긴 텍스트 내에서 특정 패턴을 효율적으로 찾을 수 있는 알고리즘으로, Z 배열을 사용하여 텍스트와 패턴의 일치 정보를 빠르게 계산합니다. 이를 통해 검색 엔진, DNA 서열 검색 등 다양한 분야에서 패턴 검색을 최적화할 수 있습니다.
Z Algorithm Explanation?
The Z Algorithm is an efficient string searching algorithm used to find a pattern within a text or calculate matching prefixes quickly. It works by constructing a Z array
, which stores the length of the longest substring starting from each position in the string that matches the prefix of the string.
What is the Z Array?
- The Z array contains the length of the longest substring starting from each index that matches the prefix of the original string.
- For example, for the string
S = "abcababc"
, the Z array would be[8, 0, 0, 2, 0, 0, 3, 0]
. The first index is 8 because the whole string matches itself, and at the fourth index, the substring matches the prefixabc
with length 2.
Use Cases:
- String Search: Used to find short patterns within large texts efficiently. Unlike the KMP algorithm, the Z algorithm processes the pattern and text in one combined string.
- Bioinformatics: Used to search for specific patterns within DNA sequences.
- Search Engines: Helps find documents containing certain patterns or keywords.
JavaScript Example Code: Z Algorithm
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
function buildZArray(s) {
const n = s.length;
const Z = Array(n).fill(0);
let l = 0,
r = 0,
k;
for (let i = 1; i < n; i++) {
if (i > r) {
l = r = i;
while (r < n && s[r] === s[r - l]) {
r++;
}
Z[i] = r - l;
r--;
} else {
k = i - l;
if (Z[k] < r - i + 1) {
Z[i] = Z[k];
} else {
l = i;
while (r < n && s[r] === s[r - l]) {
r++;
}
Z[i] = r - l;
r--;
}
}
}
return Z;
}
function zSearch(text, pattern) {
const concat = pattern + "$" + text;
const Z = buildZArray(concat);
const m = pattern.length;
for (let i = 0; i < Z.length; i++) {
if (Z[i] === m) {
console.log("Pattern found at index", i - m - 1);
}
}
}
// Example Usage
const text = "abcabcababcabc";
const pattern = "abc";
zSearch(text, pattern); // Output: Pattern found at index 0, 3, 6, 9
Explanation:
- Building the Z Array: The Z array is calculated for the combined string of pattern and text. It calculates how much of the substring starting from each index matches the prefix.
- Pattern Search: The concatenated string is processed, and wherever the Z array matches the length of the pattern, a match is found in the original text.
Rust Example Code: Z Algorithm
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
fn build_z_array(s: &str) -> Vec<usize> {
let n = s.len();
let mut z = vec![0; n];
let s_bytes = s.as_bytes();
let (mut l, mut r, mut k);
for i in 1..n {
if i > r {
l = i;
r = i;
while r < n && s_bytes[r] == s_bytes[r - l] {
r += 1;
}
z[i] = r - l;
r -= 1;
} else {
k = i - l;
if z[k] < r - i + 1 {
z[i] = z[k];
} else {
l = i;
while r < n && s_bytes[r] == s_bytes[r - l] {
r += 1;
}
z[i] = r - l;
r -= 1;
}
}
}
z
}
fn z_search(text: &str, pattern: &str) {
let concat = format!("{}${}", pattern, text);
let z = build_z_array(&concat);
let m = pattern.len();
for i in 0..z.len() {
if z[i] == m {
println!("Pattern found at index {}", i - m - 1);
}
}
}
fn main() {
let text = "abcabcababcabc";
let pattern = "abc";
z_search(text, pattern); // Output: Pattern found at index 0, 3, 6, 9
}
Explanation:
- Building the Z Array: In Rust, we convert the string to a byte array and calculate the Z array based on the prefix match of each substring.
- Pattern Search: The concatenated string (pattern + text) is used to search for the pattern in the text by finding matches in the Z array.
Summary:
The Z algorithm is a highly efficient algorithm used to search for patterns within a text by leveraging the Z array. This method is useful in a variety of fields, such as search engines, DNA sequence matching, and text processing, where pattern recognition is critical.