문자열 ( S )가 주어졌을 때:
입력
출력
import java.util.*;
public class Main {
static String s;
static int[] suffixArray, rank, lcp, pi;
static int n;
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
s = sc.next();
n = s.length();
buildSuffixArray();
buildLCPArray();
buildPiArray();
int[] dp = new int[n];
Arrays.fill(dp, -1);
List<int[]> results = new ArrayList<>();
results.add(new int[]{n, 1});
int x = pi[n - 1];
while (x > 0) {
int index = rank[n - x];
results.add(new int[]{x, calculateCount(index, dp)});
x = pi[x - 1];
}
System.out.println(results.size());
for (int i = results.size() - 1; i >= 0; i--) {
System.out.println(results.get(i)[0] + " " + results.get(i)[1]);
}
}
static void buildSuffixArray() {
suffixArray = new int[n];
rank = new int[n];
int[] tempRank = new int[n];
Integer[] order = new Integer[n];
for (int i = 0; i < n; i++) {
suffixArray[i] = i;
rank[i] = s.charAt(i) - 'A';
order[i] = i;
}
for (int length = 1; length < n; length *= 2) {
int finalLength = length;
Arrays.sort(order, (a, b) -> {
if (rank[a] != rank[b]) return Integer.compare(rank[a], rank[b]);
int rankA = (a + finalLength < n) ? rank[a + finalLength] : -1;
int rankB = (b + finalLength < n) ? rank[b + finalLength] : -1;
return Integer.compare(rankA, rankB);
});
for (int i = 0; i < n; i++) {
suffixArray[i] = order[i];
}
tempRank[suffixArray[0]] = 0;
for (int i = 1; i < n; i++) {
boolean sameRank = rank[suffixArray[i]] == rank[suffixArray[i - 1]]
&& ((suffixArray[i] + length < n ? rank[suffixArray[i] + length] : -1)
== (suffixArray[i - 1] + length < n ? rank[suffixArray[i - 1] + length] : -1));
tempRank[suffixArray[i]] = tempRank[suffixArray[i - 1]] + (sameRank ? 0 : 1);
}
System.arraycopy(tempRank, 0, rank, 0, n);
}
}
static void buildLCPArray() {
lcp = new int[n];
int h = 0;
for (int i = 0; i < n; i++) {
if (rank[i] > 0) {
int j = suffixArray[rank[i] - 1];
while (i + h < n && j + h < n && s.charAt(i + h) == s.charAt(j + h)) {
h++;
}
lcp[rank[i]] = h;
if (h > 0) h--;
}
}
}
static void buildPiArray() {
pi = new int[n];
int j = 0;
for (int i = 1; i < n; i++) {
while (j > 0 && s.charAt(i) != s.charAt(j)) {
j = pi[j - 1];
}
if (s.charAt(i) == s.charAt(j)) {
j++;
}
pi[i] = j;
}
}
static int calculateCount(int x, int[] dp) {
if (dp[x] != -1) return dp[x];
dp[x] = 1;
for (int i = x + 1; i < n; i++) {
if (lcp[i] >= n - suffixArray[x]) {
dp[x] += calculateCount(i, dp);
i += calculateCount(i, dp) - 1;
} else {
break;
}
}
return dp[x];
}
}
Suffix Array 구성
LCP Array 생성
KMP의 Prefix Function 생성
등장 횟수 계산
이 코드는 문자열의 Prefix와 Suffix를 효율적으로 탐색하며, 여러 자료구조(Suffix Array, LCP Array, KMP)를 조합해 문제를 해결한다. 이를 통해 시간 복잡도를 𝑂(𝑁log𝑁)수준으로 유지하며 대규모 입력에도 대응할 수 있다.
구현 과정에서 Suffix Array 정렬과 등장 횟수 계산 로직의 디테일을 이해하는 데 고민이 많았으나, 이를 통해 문자열 처리의 고급 알고리즘을 깊이 이해할 수 있었다.