Suffix Array, LCP Array¶
Problem¶
Definition 1
- \(SA[i] := S\)의 suffix들을 사전순으로 정렬했을 때, \(i\)번째 등장하는 suffix의 시작 인덱스
- \(R[i] := i\)에서 시작하는 suffix의 사전순 비교 순서, \(R[SA[i]]=i\)
- \(LCP[i] :=\) \(SA[i]\)에서 시작하는 suffix와 \(SA[i-1]\)에서 시작하는 suffix의 longest common prefix의 길이
\(LCP[i] = LCP(S[SA[i-1]...], S[SA[i]...])\) \((2 \le i)\)
- Suffix Array, LCP Array : 문자열 \(S\)가 주어질 때, \(S\)의 suffix array, LCP array를 구한다.
- Compare Substring : 문자열 \(S\)의 substring \(S[l_1 \cdots r_1]\), \(S[l_2 \cdots r_2]\)를 사전순으로 비교한다.
Algorithm¶
-
Suffix Array
각 suffix들의 앞의 \(1, 2, 4, 8, \cdots\) 개의 문자들만을 생각했을 때의 정렬 순서를 순차적으로 구하며 \(SA[i], R[i]\) 배열을 관리한다. 가장 먼저 각 suffix들의 앞의 \(1\)개의 문자에 대한 정렬 순서, 즉 알파벳 순으로 정렬하여 \(SA[i], R[i]\)를 구하고 시작한다. 각 suffix들의 앞의 \(d\) 개의 문자들에 대한 정렬 순서를 알고 있다 하였을 때 길이 \(2d\)에 대한 순서를 구하기 위하여, \(i\)에서 시작하는 suffix의 순서는 순서쌍 \((R[i], R[i+d])\)의 순서와 같음을 이용한다. 먼저 \(R[i+d]\)의 순서대로, 그 후 \(R[i]\)의 순서대로 counting sort를 하여 새로운 \(SA[i]\)를 구하고, 이를 이용하여 새로운 \(R[i]\)를 구한다.
Complexity
\(O(|S|log|S|)\)
-
LCP Array
Property 1
\[LCP[R[i]]-1 \le LCP[R[i+1]]\]suffix의 길이가 감소하는 순서, 즉 \(i\)가 증가하는 순서대로 \(LCP[R[i]]\)를 채운다. Property 1에 의해 \(LCP[R[i]]=LCP[R[i-1]]-1\)부터 시작하여 늘릴 수 있을 때까지 naive하게 늘려본다.
Complexity
\(O(|S|)\)
-
Compare Substring
Suffix array를 구하는 과정에서, 각 길이 \(d\)에 대한 \(R[i]\)를 \(R2[i][d]\)에 기록한다. \(R2[i][d]\)를 sparse table과 같이 사용하여, \(O(1)\) RMQ 알고리즘과 같이 두 substring을 비교한다.
Complexity
Time Complexity : \(O(1)\)
Space Complexity : \(O(|S|log|S|)\)
Code¶
Details¶
MAXN
이 선언되어야 함void SA_LCP(string S)
: \(S\)의 suffix array, LCP array를 구함 (SA
,R
,LCP
배열을 채움)- \(S\)는 1-based (leading "?")
bool cmp(int l1, int r1, int l2, int r2)
: \(S[l_1 \cdots r_1]\), \(S[l_2 \cdots r_2]\)를 사전순으로 비교하여 \(S[l_1 \cdots r_1] < S[l_2 \cdots r_2]\)를 리턴함- 사용하기 위해서
void SA_LCP(string S)
의 코드 내 주석을 제거하고 호출했어야 함
- 사용하기 위해서