DY N DY

BOJ 9251 LCS(C++) 본문

PARK/ALGORITHM

BOJ 9251 LCS(C++)

손세지 2016. 10. 11. 11:59
시간 제한메모리 제한제출정답맞은 사람정답 비율
1 초128 MB208493266142.728%

문제

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

예를 들어, ACAYKP와 CAPCAK의 LCS는 ACAK가 된다.

입력

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

출력

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

예제 입력 

ACAYKP
CAPCAK

예제 출력 

4

힌트

출처


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#include    <string>
#include    <iostream>
#pragma warning(disable:4996)
#define max(x,y) ((x) < (y) ? (y) : (x))
using namespace std;
int D[1111][1111];
 
int main()
{
    ios::sync_with_stdio(false);
    string A, B;
    cin >> A >> B;
 
    D[0][0] = (A[0] == B[0]);
    for (int i = 1; i < A.length(); ++i)
        D[i][0] = max(D[i - 1][0], (A[i] == B[0]));
    for (int i = 1; i < B.length(); ++i)
        D[0][i] = max(D[0][i - 1], (A[0] == B[i]));
    for (int i = 1; i < A.length(); ++i)
    {
        for (int j = 1; j < B.length(); ++j)
        {
            if (A[i] == B[j])
                D[i][j] = D[i - 1][j - 1] + 1;
            else
                D[i][j] = max(D[i - 1][j], D[i][j - 1]);
        }
    }
 
    cout << D[A.length() - 1][B.length() - 1] << endl;
 
    return 0;
}


유명한 문제라고 들었다. 

다이나믹 프로그래밍으로 해결. 


기본적인 점화식은 다음과 같다. 

I길이의 A수열과

J길이의 B수열이 있을 때

A의 i번째, B의 j번째까지의 최장공통부분수열(LCS)는 

D[i][j]로 나타내고 이는 두가지로 나눌 수 있다. 


1. A[i]번째와 B[j]번째가 동일한 경우 

D[i-1][j-1] + 1이 된다. A[i]와 B[j]가 동일하기 때문에 그 이전까지의 최대값 + 1이 i,j번째까지의 최대값이 된다.

2. A[i]번째와 B[j]번째가 동일하지 않은 경우

이 경우에는 D[i][j-1]과 A[i-1][j] 중 큰 값이 오게 된다. D[i-1][j-1]은 이미 D[i][j-1], D[i-1][j]에서 고려되었기 때문에 두 후보 중에 더 큰 값을 선택하면 된다. 


사실 이 문제에서 어렵게 느껴졌던 것은 14~18라인 이었는데

특히 14라인의 D[0][0]을 초기화하지 않아서 왜 틀린지도 모르고 멘붕이었다. 

이건 구현상의 문제이므로 문제를 푸는 사람에 따라 for loop 내에서 자동으로 연산될 수도 있겠지만... 


15~18라인은  

초기화 부분인데 A[0]과 B전체, B[0]과 A전체를 비교해서 미리 계산을 해두어야 한다. 

그래야 19라인의 for loop에서 계산이 가능하다. 

생각해보면 간단한데

19라인의 for loop는 

i-1, j-1을 참조하므로 

 for (int i = 1; i < A.length(); ++i)
        for (int j = 1; j < B.length(); ++j)
        

위와 같이 1부터 시작할수밖에 없다. 

그렇다면 0번째를 계산해야 하고 자연스럽게 15~18라인을 먼저 계산해주어야 한다.


그외에는 다른 다이나믹 문제와 크게 다를게 없다. 




'PARK > ALGORITHM' 카테고리의 다른 글

실력키우기 숫자 야구(C++)  (3) 2016.10.25
알고리즘 & BOJ 2616 소형기관차(C++)  (2) 2016.10.21
실력키우기 색종이(고)(C++)  (1) 2016.09.29
실력키우기 연속부분최대곱(C++)  (1) 2016.09.28
koitp 세금(TAX)(C++)  (0) 2016.09.27