이 문제의 핵심은 '부분 수열(Subsequence)'의 정의에 있습니다. '부분 문자열(Substring)'과 달리 문자들이 반드시 인접해 있을 필요가 없습니다.
만약 우리가 두 문자열의 모든 부분 수열을 생성하여 비교하는 브루트 포스(Brute-force) 방식을 택한다면, 길이가 인 문자열의 부분 수열 개수는 개이므로 시간 복잡도는 이라는 처참한 수치를 기록하게 됩니다. 인 이 문제에서는 절대 불가능한 방식이죠. 따라서 우리는 최적 부분 구조(Optimal Substructure)를 찾아 하위 문제의 해를 재활용하는 DP로 선형 환원을 시도해야 합니다.
두 문자열 와 를 비교할 때, 각 문자열의 끝에서부터 비교하며 경우의 수를 나눕니다.
이를 위해 2차원 배열 를 선언하며, 이는 '문자열 A의 번째 문자까지와 문자열 B의 번째 문자까지 고려했을 때의 LCS 길이'를 의미하게 됩니다.
문자열 의 번째 문자를 , 문자열 의 번째 문자를 라고 할 때 점화식은 다음과 같습니다.
입력받은 로직을 바탕으로 가독성을 정리한 Java 11 코드입니다.
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
class Main {
static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
public static void main(String[] args) throws IOException {
// 문자열을 char 배열로 변환하여 접근성 향상
char[] A = br.readLine().toCharArray();
char[] B = br.readLine().toCharArray();
// 인덱스 에러 방지 및 초기값(0) 처리를 위해 크기를 +1 설정
int[][] dp = new int[A.length + 1][B.length + 1];
for (int i = 1; i <= A.length; i++) {
for (int j = 1; j <= B.length; j++) {
// 문자가 같은 경우: 대각선 위(이전 단계) 값 + 1
if (A[i - 1] == B[j - 1]) {
dp[i][j] = dp[i - 1][j - 1] + 1;
}
// 문자가 다른 경우: 왼쪽 혹은 위쪽 값 중 최댓값 선택
else {
dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]);
}
}
}
// 최종 결과 출력
System.out.println(dp[A.length][B.length]);
}
}
length + 1로 설정하는 것은 매우 전형적이지만 강력한 테크닉입니다. 이는 혹은 참조 시 발생할 수 있는 IndexOutOfBounds 예외를 방지하며, 빈 문자열과의 비교 결과(0)를 자연스럽게 기저 사례(Base Case)로 포함시킵니다.