https://www.acmicpc.net/problem/9251
DP (다이나믹 프로그래밍)
LCS(Longest Common Subsequence, 최장 공통 부분 수열)문제는 두 수열이 주어졌을 때, 모두의 부분 수열이 되는 수열 중 가장 긴 것을 찾는 문제이다.
예를 들어, ACAYKP와 CAPCAK의 LCS는 ACAK가 된다.
첫째 줄과 둘째 줄에 두 문자열이 주어진다. 문자열은 알파벳 대문자로만 이루어져 있으며, 최대 1000글자로 이루어져 있다.
ACAYKP
CAPCAK
첫째 줄에 입력으로 주어진 두 문자열의 LCS의 길이를 출력한다.
4
DP로 풀어야 하는 이유부터 생각을 해보자.
먼저 완전 탐색으로 풀었을 때 무슨 문제가 있길래 DP를 가져와서 풀어야 하는 것일까?
N개의 주어진 문자열을 선택하거나, 선택하지 않거나 두가지 경우가 존재하고, 그 개수를 직접 세어보면 된다.
즉 나올 수 있는 수로는 O(2^n x 2^n x N) 되며.. 엄청나게 큰 수가 발생하여 완전탐색은 불가능하다는 것을 확인할 수 있었다.
그렇다면 DP를 이용하여 풀게 되면 어떻게 해야 할까..
가지고 있는 공통 배열을 나열해서 하나씩 차례대로 보면,
s1에서 가져온 값과 s2에서 가져온 값을 비교해 볼 수 있을 것이다.
직관적으로 LCS가 최대 4가 될 수 있다는 것을 이해할 수 있다.
그런데, 이 LCS값을 더하는 기준을 어떠게 할 것인가?가 이 문제의 어려운 포인트라고 할 수 있는 것 같다.
K로 같기 때문에 전 단계의 문자열을 비교하고 같은 것이 겹치는 것끼리를 계산하여 LCS에다가 1을 더하면 된다.
예시로 들 수 있는 상황은 아래와 같은 상황이다.
ACAY
CAPCA
첫번째 시도로는 두번째 배열의 A를 제거해보고 일치하는 문자열 개수를 찾아본다. 그러면 ACA
와, AC
가 존재하므로 LCS 값이 2가 된다.
두번째 시도로는 첫번재 배열의 Y를 제거해보고 일치하는 문자열의 개수를 찾아본다. ACA
, ACA
두개가 존재하므로 LCS값은 3이 된다. 이 둘 중에 Max값을 찾아 정답으로 내놓으면 된다.
코드는 아래의 순서로 작성할 수 있다.
import sys
read = sys.stdin.readline
# 두개의 문자열을 받아온다.
s1, s2 = read().strip(), read().strip()
# s1, s2를 행과 열로 삼아 dp 2차원 테이블을 만든다.
dp = [[0 for j in range(len(s2) + 1)] for i in range(len(s1) + 1)]
for i in range(1, len(s1) + 1):
for j in range(1, len(s2) + 1):
if s1[i - 1] == s2[j - 1]:
dp[i][j] = dp[i - 1][j - 1] + 1
else:
dp[i][j] = max(dp[i - 1][j], dp[i][j - 1])
# 가장 마지막 행 출력!
print(max(max(dp)))
어려운 문제이기도 하고 기본이 되는 문제이니만큼 꼼꼼하게 복습해두자.