💻 백준 9251번: LCS (최장 공통 부분 수열)
- 문제 링크: https://www.acmicpc.net/problem/9251
- 알고리즘 분류: 다이나믹 프로그래밍
문제 소개 🧐
두 문자열이 주어졌을 때, 두 문자열 모두의 ‘부분 수열’이 되는 수열 중 가장 긴 것(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를 통해 어떻게 효율적으로 해결할 수 있는지 체감할 수 있었습니다.