PS(Problem Solving)/백준(BOJ)

[1958번][C/C++] LCS 3

seongmik 2023. 6. 27. 22:00
728x90
반응형

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

 

1958번: LCS 3

첫 줄에는 첫 번째 문자열이, 둘째 줄에는 두 번째 문자열이, 셋째 줄에는 세 번째 문자열이 주어진다. 각 문자열은 알파벳 소문자로 이루어져 있고, 길이는 100보다 작거나 같다.

www.acmicpc.net

 

접근

처음에는 LCS가 무조건 이어진 문자열 이어야 한다고 생각해서 이어진 문자열에 대한 코드를 작성했다.

악랄하게도 주어진 예제도 그렇게 이해하고 풀 수 있는 예제였다 ㅠ

예제 입력
abc
abc
ac
예제 출력
2

a, b가 떨어져있더라도 그냥 띄어서 개수를 셀 수 있다는 것이다..

위와 같은 반례가 있기 때문에 다르게 다 풀고서 처음부터 다시 접근했다.


해결

기본적으로 DP로 풀어야한다는 감이 온다. (aka. 직감)

DP[첫번째 문자열에서의 Index][두번째 문자열에서의 Index][세번째 문자열에서의 Index] = 각 문자열의 인덱스에서부터 이전까지의 3개의 문자열의 최대 LCS길이로 설정하고 풀면 된다.

 

예시를 보고 작동을 이해해보자.

a b c
a b c
a c  

와 같이 2차원 vector v에 문자열 3개가 들어있다.

(DP[0][0][0], DP[1][0][0], DP[0][1][0], DP[0][0][1]은 0으로 놔두고 시작한다.)

 

b c
a b c
a c  

v[0][0] == v[1][0] == v[2][0] 이므로 DP[1][1][1] = DP[0][0][0] + 1이다. 

즉, DP[1][1][1] = 1

 

b c
a b c
a c  

v[0][0] == v[1][0] != v[2][1] 이므로 DP[1][1][2] = max(DP[0][1][2], DP[1][0][2], DP[1][1][0])

즉, DP[1][1][2] = 0

 

b b
a b b
a c  

v[0][0] != v[1][1], v[1][1] != v[2][0] 이므로

DP[1][2][1] = 0

 

 

... 쭉 DP[i][j][k] = 0이 다음과 같이 대입된다.

그러다가

b c
a b c
a c  

v[0][2] == v[1][2] == v[2][1] 이므로

DP[3][3][2] = DP[2][2][1] + 1이다.

DP[2][2][1]은 max(DP[1][2][1], DP[2][1][1], DP[2][2][0]) 인데

이 중 DP[1][2][1] = max(DP[0][2][1], DP[1][1][1], DP[1][2][0]) 이다.

마지막으로 DP[1][1][1]은 처음에 구한 1이기 때문에 거슬러 올라가면

DP[3][3][2] = DP[2][2][1] + 1 는

DP[3][3][2] = 2가 된다.

 

즉, 이전에 (i, j ,k) 세 인덱스의 이전 string 3개의 LCS의 최댓값을 저장해서 그 이후 (i, j, k)조합에서 이전을 계산하지 않고 DP값을 이용해서 이후에 이어 붙일 수 있게 구현한 아이디어이다.


코드

#include <iostream>
#include <algorithm>
#include <vector>
#include <queue>
#include <utility>
#include <cmath>

using namespace std;
vector<string> v;
int DP[101][101][101];

bool compare(string a, string b) {
    return a.length() > b.length();
}

int main() {
    string str1, str2, str3;
    cin >> str1 >> str2 >> str3;
    v.push_back(str1);
    v.push_back(str2);
    v.push_back(str3);
    sort(v.begin(), v.end(), compare);
    int len1 = v[0].length(), len2 = v[1].length(), len3 = v[2].length();

    for (int i=1;i<=len1;i++) {
        for (int j=1;j<=len2;j++) {
            for (int k=1;k<=len3;k++) {
                if (v[0][i - 1] == v[1][j - 1] && v[1][j - 1] == v[2][k - 1]) {
                    DP[i][j][k] = DP[i - 1][j - 1][k - 1] + 1;
                }
                else {
                    DP[i][j][k] = max(DP[i-1][j][k], max(DP[i][j-1][k], DP[i][j][k-1]));
                }
            }
        }
    }

    cout << DP[len1][len2][len3];
    
    return (0);
}

처음에 이어진 경우만 되는 줄 알고 구현할 때 sort를 이용해서 문자열의 길이를 정렬한 뒤에 풀었던 흔적이 남아있는데 이 로직에서는 의미가 없다. 그냥 v벡터로 구현했기 때문에 남겨준 줄이다.


문제가 너무 안 풀려서 오랜만에 다른 사람의 글을 보고 이해해서 푼 문제였다.

DP부분에서는 확실히 내가 약한 것 같다고 느끼고 DP와 문자열문제를 더 풀어야겠다고 생각했다.

728x90
반응형

'PS(Problem Solving) > 백준(BOJ)' 카테고리의 다른 글

[C/C++] 26495번 - Big Number  (0) 2024.03.31
[1550번][C/C++] 16진수  (1) 2023.02.03
[2239번][C/C++] 스도쿠  (1) 2023.02.02
[1038번][C/C++] 감소하는 수  (0) 2023.02.01
[2342번][C/C++] Dance Dance Revolution  (0) 2023.01.31