Problem Solving

Z 알고리즘

limdef 2024. 2. 10. 20:21

Z[i] : s의 i 부터의 substring == s의 prefix 가 되는 최대 길이

 

Z 배열 :

문자열 s의 Z배열 z[i]는 s[i]에서 시작하는 부분 문자열이면서 s의 prefix이기도 한 가장 긴 문자열의 길이를 담는 배열이다.

이전에 구한 정보들을 최대한 활용해서 O(n)에 Z배열을 구할 수 있다.

 

 

문자열 ABCABCABAB인 경우 Z 배열

A B C A B C A B A B
x 0 0 5 0 0 2 0 2 0

 

 

vector<int> z_function(const string &s) {
    int n = s.size();
    vector<int> z(n);
    z[0] = n;
    int L = 0, R = 0;
    for (int i = 1; i < n; i++) {
        if(R<i) {
            L = R = i;
            while (R<n && s[R-L] == s[R]) {
                R++;
            }
            z[i] = R-L; R--;
        }
        else {  // s[i-L...R-L] = s[i...R]
            if(i + z[i-L] -1 < R) {
                z[i] = z[i-L];   
            }
            else {
                L = i;
                while(R<n && s[R-L] == s[R]) {
                    R++;
                }
                z[i] = R-L;
                R--;
            }
        }
    }
    return z;
}

 

 

prefix과 일치하는 substring 중 가장 긴 substring의 인덱스 l,r 중 r이 가장 클 때의 l,r을 L, R에 유지한다.

 

1. R < i 

i에서 활용가능한 정보가 없기 때문에 s[0]과 s[i]에서 하나씩 비교하며 L,R을 갱신한다.

일치하는 게 없다면 L=i, R=i

일치하는게 있다면 L=i, R = i+z[i]-1

 

2. i <= R

i가 [L,R] 구간에 존재하고, s[0...R-L] == s[L...R] 이므로 s[i-L...R-L] == s[i...R].

이는 미리 앞에서 구해놓은 z[i-L]을 활용할 수 있다는 의미.

 

2.1  i + z[i-L] - 1 < R 인 경우

s[i...R] 에서 가장 길어봐야 z[i-L]인 것을 앞서 s[i-L...R-L] 의 정보를 통해서 알 수 있고 다시 구할 필요가 없음

z[i] = z[i-L]

 

2.2 i + z[i-L] - 1 >= R 인 경우

R까지는 prefix가 일치함을 알 수 있지만 R 이후로는 알 수 없으므로 R 이후의 인덱스에 대해 한 글자씩 비교한다.