💻 백준 9251번: LCS (최장 공통 부분 수열)


문제 소개 🧐

두 문자열이 주어졌을 때, 두 문자열 모두의 ‘부분 수열’이 되는 수열 중 가장 긴 것(LCS, Longest Common Subsequence)의 길이를 찾는 문제이다.

입력 📝

첫째 줄과 둘째 줄에 두 문자열이 주어진다. 문자열은 알파벳 대문자로만 이루어져 있으며, 최대 1000글자로 이루어져 있다.

출력 📤

첫째 줄에 입력으로 주어진 두 문자열의 LCS의 길이를 출력한다.

제한 ❌

  • 시간: 2초
  • 메모리: 256 MB

나의 풀이 과정 🌊

첫 번째 시도: 브루트포스 + 조합

Idea: 한 문자열의 모든 부분 수열 조합을 만들어, 다른 문자열에 포함되는지 확인하는 방식을 생각했습니다. 하지만 문자열 길이가 1000일 때 조합의 수는 기하급수적으로 늘어나 시간 내에 풀 수 없었다.

import sys
from collections import deque
from itertools import combinations

a = sys.stdin.readline().rstrip()
b = sys.stdin.readline().rstrip()
length = len(b)
answer = 0

for i in range(len(a), -1, -1):    
    str_li = list(combinations(list(a), i))
    for string in str_li:
        prev = -1
        is_same = True
        new_b = b
        for let in string:
            idx = new_b.find(let)
            if idx != -1:
                new_b = b[idx+1:length+1]
            else:
                is_same = False
                break
        if is_same:
            print(i)
            exit()

Result: 시간 초과 😭


두 번째 시도: 2차원 DP ✨

Idea: LCS 문제의 정석적인 풀이인 2차원 DP 테이블을 사용했다. dp[i][j]는 ‘첫 번째 문자열의 i번째 글자까지’와 ‘두 번째 문자열의 j번째 글자까지’의 LCS 길이를 의미한다.

  • str1[i-1] == str2[j-1] 이면, 두 문자가 같으므로 대각선 위 값에 1을 더한다. (dp[i-1][j-1] + 1)
  • 다르면, 위쪽 값과 왼쪽 값 중 더 큰 값을 가져온다. (max(dp[i-1][j], dp[i][j-1]))

Code:

import sys

str1 = sys.stdin.readline().rstrip()
str2 = sys.stdin.readline().rstrip()

dp = [[0] * (len(str2) + 1) for _ in range(len(str1) + 1)]

for i in range(1, len(str1) + 1):
    for j in range(1, len(str2) + 1):
        if str1[i-1] == str2[j-1]:
            dp[i][j] = dp[i-1][j-1] + 1
        else:
            dp[i][j] = max(dp[i][j-1], dp[i-1][j])

print(dp[-1][-1])

Result: 성공!


배운 점 및 느낀 점 ✍️

  • LCS 알고리즘의 2차원 DP 점화식을 확실하게 익힐 수 있었다.
  • 브루트포스로는 해결할 수 없는 문제를 DP를 통해 어떻게 효율적으로 해결할 수 있는지 체감할 수 있었습니다.