본문 바로가기

백준 문제 풀이

백준 9251번 LCS(C++)

728x90

https://www.acmicpc.net/problem/9251

 

9251번: LCS

LCS(Longest Common Subsequence, 최장 공통 부분 수열)문제는 두 수열이 주어졌을 때, 모두의 부분 수열이 되는 수열 중 가장 긴 것을 찾는 문제이다. 예를 들어, ACAYKP와 CAPCAK의 LCS는 ACAK가 된다.

www.acmicpc.net

 

최장 공통 수열이란 

ABCDE

BCFZ E

 

두개가 있으면 BCE가 공통 수열이다. 즉 이어져있지 않아도 순서만 맞으면 된다는 뜻이다.

이 문제는 DP분류이지만 LCS알고리즘이라는 말이 따로 있을 정도라 카테코리로 분류해도 된다고 생각이 든다. 처음에는 2 x N 배열로 문제를 해결하려 했는데 사실 알고보니 두 문자열의 길이가 다를수도 있어서 될수가 없는 풀이였다. 결국 구글링해보니 2차원 배열을 N * M 선언하여 풀이하는 문제였다.

 

배열을 선언하면 다음과 같다.

DP[] 0 A(1) C(2) A(3) Y(4) K(5) P(6)
0 0 0 0 0 0 0 0
C(1) 0            
A (2) 0     X      
P (3) 0            
C (4) 0            
A (5) 0            
K (6) 0            

 

여기서 DP[2][3] 이라하면 위 테이블에서 X 친 부분이 될 것이다. 이 부분에 들어갈 정보는 CA와 ACA를 비교했을 때의 최장 공통 수열이 들어간다. 

 

DP[N][M] = N번째까지의 문자열1과 M번째 까지의 문자열2를 비교했을 때 최장 공통수열의 길이

 

그렇다면 어떻게 비교를 해야할까.

먼저 가장 윗줄을 채워보자. 

DP[1][1] = C와 A의 비교이다. 이 둘은 다르므로 길이는 0이다.

DP[1][2] = C와 AC의 비교이다. 여기서 C가 같다! 그러므로 1을 채운다. 

DP[1][3] = C와 ACA의 비교이다. 여기서 A는 C와 다르지만, 우리는 순서가 중요하지 연속은 상관없기 때문에 이전값을 그대로 넣는다.

DP[] 0 A(1) C(2) A(3) Y(4) K(5) P(6)
0 0 0 0 0 0 0 0
C(1) 0 0 1 1 1 1 1
A (2) 0            
P (3) 0            
C (4) 0            
A (5) 0            
K (6) 0            

 

다음으로 2행을 채워보자

DP[2][1] = CA와 A의 비교이다. A가 같으므로 1을 채운다.

DP[2][2] = CA와 AC의 비교이다. 이전값이 1이었으므로 1을 채운다.

DP[2][3] = CA와 ACA의 비교이다. 앗! 이번에는 같은 A가 만났다. 그렇다면, 이전 값 + 1일까? 아니다. DP[i - 1][j - 1] + 1이다. 왜냐하면 CA, ACA의 최장 공통 수열의 값은 C와 AC의 최장 공통 수열 + 1과 같기 때문이다. 

만약 옆 행이나 옆 열 + 1 을 한다면 만약 CA, ACAA 의 비교를 했을 때 최장 길이가 2가 아니라 또 A가 나왔다고 생각해서 3이 됐을 것이다. 

 

DP[] 0 A(1) C(2) A(3) Y(4) K(5) P(6)
0 0 0 0 0 0 0 0
C(1) 0 0 1 1 1 1 1
A (2) 0 1 1 2 2 2 2
P (3) 0            
C (4) 0            
A (5) 0            
K (6) 0            

 

3행을 채워보면 다음과 같다.

DP[3][1] = CAP와 A의 비교이다. 여기서 우리는 지금까지 왼쪽 값을 가져오는 것 처럼 보였다. 그러나 윗 행의 값도 참조해야한다. 왜냐하면 CAP와 A의 비교는 CA와 A의 비교와 같을 수 밖에 없다. 만약 왼쪽만 참조한다면, 이 값은 0이됐을 것이다.

 

다시말해 ABC, BCA를 비교하는 연산이라고 가정하면 ABC와 BC를 비교한것과 AB와 BCA를 비교한 것 둘 중 큰값을 가져와야한다.

 

P는 시간을 빨리 돌려 DP[3][6]을 살펴보자 이전값들은 모두 1이 될 것이다.

CAP와 ACAYKP의 비교이다.  2행을 채웠을 때 같은 값을 만난것을 복기해보면 DP[i - 1][j - 1] + 1 이므로 (CA와 CACYKP의 최장 공통 수열의 길이 + 1 이므로) 3이된다.

그리고 나머지까지 모두 채워보면 다음과 같다.

DP[] 0 A(1) C(2) A(3) Y(4) K(5) P(6)
0 0 0 0 0 0 0 0
C(1) 0 0 1 1 1 1 1
A (2) 0 1 1 2 2 2 2
P (3) 0 1 1 2 2 2 3
C (4) 0 1 2 2 2 2 3
A (5) 0 1 2 3 3 3 3
K (6) 0 1 2 3 3 4 4

 

그러므로 마지막 DP[N][M] 이 답이다.

 

이제 이를 바탕으로 점화식을 새워보자.

위의 케이스는 2개가 있었다. 두 문자열의 이번 비교 문자가 같은 경우와 다른 경우.

그리고 같을 때는 각 문자열의 이전 문자열 까지의 최장공통수열 + 1을 하였고

다를 경우에는 각 문자열의 이전값들 중 큰 값을 가져왔다.

 

if(문자열1[i] == 문자열2[j]) DP[i][j] = DP[i - 1][j - 1] + 1
else DP[i][j] = max(DP[i - 1][j] , DP[i][j - 1]

 

정답코드

 

#include <iostream>

using namespace std;

int dp[1002][1002];

int main() {
	ios_base::sync_with_stdio(false);
	cin.tie(NULL);

	string str1, str2;

	cin >> str1 >> str2;

	int N = str1.size();
    int M = str2.size();

	for (int i = 1; i <= N; i++) {
		for (int j = 1; j <= M; j++) {
			if (str1[i - 1] == str2[j - 1]) dp[i][j] = dp[i - 1][j - 1] + 1;
			else dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);
		}
	}

	cout << dp[N][M] << '\n';
}

 

'백준 문제 풀이' 카테고리의 다른 글

백준 11000번 강의실 배정 (C++)  (2) 2023.12.05
백준 2457번 공주님의 정원 (C++)  (1) 2023.12.05
백준 15683번 - 감시(C++)  (0) 2023.12.01
백준 1759번 암호 만들기  (0) 2023.11.29
백준 9465번 - 스티커(C++)  (0) 2023.11.29