Algorithm/알고리즘 백준 풀이

[백준] #9252 LCS2(Longest Common Subsequence) - (DP)

i009727 2020. 3. 2. 17:01

문제

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

 

9252번: LCS 2

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

www.acmicpc.net

목표

두개의 문자가 주어졌을때 두 문자의 부분수열중 최장길이의 부분수열을 구하는 문제

풀이

dp를 사용하여 각 구간마다 최장부분수열의 길이를 저장시켜놓는다.

사진에서 동그라미 친 부분은 두 문자열의 부분문자가 같은 구간 => dp값이 1증가해야한다.

점화식

dp[i][j] = if (s1[i] == s2[j])   dp[i-1][j-1] + 1;

         = else                   max( dp[i-1][j], dp[i][j-1] );

 

코드

1. 재귀로구현

#include <iostream>
#include <cstring>
#include <stack>
#define MAX 1001

using namespace std;

//dp[i][j]: i행 j열위치에서 확인한 LCS의 최대값
int dp[MAX][MAX];
string str1;
string str2;
stack<char> s;

//지나간 dp배열값은 모두 0이상의 값으로 초기화됨 => 트래킹 가능
int func(int i, int j) {
	//문자열을 비교할수있는 범위를 넘어가면 0으로 초기화
	if (i <= 0 || j <= 0)
		return dp[i][j] = 0;
	int &ref = dp[i][j];
	if (ref != -1)
		return ref;
	//문자열의 문자가 서로 같은경우 대각선의 dp값 + 1
	if (str1[i-1] == str2[j-1]) {
		ref = func(i-1, j-1) + 1;
	}
	//문자열의 문자가 서로 다른경우 dp의 왼쪽, 위쪽중 최대값을 가짐
	else {
		ref = max(func(i, j-1), func(i-1, j));
	}
	return ref;
}
int main() {
	cin >> str1 >> str2;
	memset(dp, -1, sizeof(dp));
	cout << func(str1.length(), str2.length()) << '\n';

	// cout << '\n' << '\n';
	// for (int i=0; i<=str1.length(); i++) {
	// 	for (int j=0; j<=str2.length(); j++) {
	// 		cout << dp[i][j] << " ";
	// 	}
	// 	cout << '\n';
	// }

	//트래킹작업
	//1. 왼쪽비교, 2. 위쪽비교 => 값이 현재값과 같다면 LCS문자열이 아니다.
	//3. 대각선비교 => 값이 현재값-1 이라면 LCS문자열이다.
	int i = str1.length();
	int j = str2.length();
	while (i>0 && j>0) {
		int tmp = dp[i][j];
		//왼쪽값과 같다면 왼쪽이동
		if (tmp == dp[i][j-1]) {
			--j;
			continue;
		}
		//위쪽값과 같다면 위쪽이동
		if (tmp == dp[i-1][j]) {
			--i;
			continue;
		}
		//대각선값+1값과 같다면 대각선이동
		if (tmp-1 == dp[i-1][j-1]) {
			--i; --j;
			s.push(str1[i]);
			continue;
		}
		return 0;
	}
	//추적한 값 출력
	while (!s.empty()) {
		cout << s.top();
		s.pop();
	}
	// cout << '\n' << '\n';
	// for (int i=0; i<=str1.length(); i++) {
	// 	for (int j=0; j<=str2.length(); j++) {
	// 		cout << dp[i][j] << " ";
	// 	}
	// 	cout << '\n';
	// }
}

 

2. 반복으로구현

#include <iostream>
#include <vector>
#include <stack>
#define MAX 1001

using namespace std;

string s1;
string s2;
int dp[MAX][MAX];
stack<char> s;

void func() {
    int len1 = s1.length();
    int len2 = s2.length();

    for (int i=1; i<=len1; ++i) {
        for (int j=1; j<=len2; ++j) {
            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]);
        }
    }
    cout << dp[len1][len2] << '\n';

    while (dp[len1][len2] != 0) {
        if (dp[len1][len2] == dp[len1][len2-1])
            len2--;
        else if (dp[len1][len2] == dp[len1-1][len2])
            len1--;
        else {
            s.push(s1[len1-1]);
            len1--;
            len2--;
        }
    }
    while (!s.empty()) {
        cout << s.top();
        s.pop();
    }
}
int main() {
    cin >> s2 >> s1;
    func();
}