출처: https://3months.tistory.com/307 [Deep Play]

백준/DP

[BOJ] 9252 LCS2 C++

코딩하는 랄뚜기 2021. 8. 1. 16:39

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

 

9252번: LCS 2

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

www.acmicpc.net

dp의 대표문제인 LCS문제를 풀었다.

dp의 핵심은 dp배열이 무엇을 의미하는지 알아내는 것이 핵심이다.

i=첫번째 문자열(s1)의 인덱스, j=두번째 문자열(s2)의 인덱스라고 할 때, dp[i][j]는 s1의 i번째 s2의 j번째까지 비교했을 때 LCS2의 최대 크기를 의미하게 된다. LCS를 구하는 알고리즘은 다음과 같다.

1. s1[i]==s2[j]라면 dp[i][j]=dp[i-1][j-1]+1

2. s1[i]!=s2[j]라면 dp[i][j]=max(dp[i-1][j],dp[i][j-1])

아, 그리고 이 문제 같은 경우에는 알파벳 대문자로만 이루어져 있기 때문에 각각의 문자열 첫번째에 '0'을 넣어주면 반복문에서 범위 때문에 넣어야 하는 조건을 빼줄 수 있다.

 

주어진 예제 ACAYKP CAPCAK로 dp[i][j]를 모두 나타내면 다음과 같다.

따라서 dp[s1.size()-1][s2.size()-1]을 출력해주면 된다.

 

이 문제에서는 LCS를 출력해 줘야 하기 때문에 dp를 역추적해야 한다.

빨간색 동그라미가 있는 경우를 출력해주면 되는데 조건은 다음과 같다.

1.s1[i]==s2[j]일 경우 문자를 저장해주고 s1의 i-1번째로 s2의 j-1번째로 간다.

2.s1[i]!=s2[j]일 경우 dp[i][j]==dp[i-1][j]라면 s1의 i-1번째로 s2의 j번째로 그렇지 않을 경우 s1의 i번째로 s2의 j-1번째로 가면 된다.

위 코드는 재귀로 쉽게 구현할 수 있다.

 

코드

#include <iostream>

#include <string>

using namespace std;

int dp[1002][1002];

char memo[1002];

string s1,s2;

 

//역추적 코드

void findlcs(int cnt,int i,int j){

    if(cnt==0) return;

    if(s1[i]==s2[j]){

        //문자열을 찾으면 저장해준다.

        memo[cnt]=s1[i];

        findlcs(cnt-1,i-1,j-1);

    }else{

        if(dp[i][j]==dp[i-1][j]) findlcs(cnt,i-1,j);

        else findlcs(cnt,i,j-1);

    }

}

 

int main(){

    cin>>s1>>s2;

    s1='0'+s1;

    s2='0'+s2;

    //LCS의 크기를 구해주는 알고리즘

    for(int i=0;i<s1.size();i++){

        for(int j=0;j<s2.size();j++){

            if(i==0||j==0){

                dp[i][j]=0;

                continue;

            }

            if(s1[i]==s2[j]){

                dp[i][j]=dp[i-1][j-1]+1;

            }else{

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

            }

        }

    }

    

    int len=dp[s1.size()-1][s2.size()-1];

    cout<<len<<'\n';

    if(len==0) return 0;

    int cnt=len;

    findlcs(len,s1.size()-1,s2.size()-1);

    for(int i=1;i<=cnt;i++) cout<<memo[i];

    return 0;

}

'백준 > DP' 카테고리의 다른 글

[BOJ] 5582 공통부분문자열  (0) 2022.02.08
[BOJ] 11066 파일 합치기  (0) 2022.02.02
[BOJ] 2096 내려가기  (0) 2022.01.31
[BOJ] 13841 It Prefokery Pio  (0) 2022.01.15
[BOJ] 1028 다이아몬드 광산  (0) 2021.08.24