Problem Solving/BOJ

[백준 / BOJ] C++ 9252 LCS 2

nageune 2023. 2. 15. 05:18
728x90
반응형

9252번: LCS 2

 

문제

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

 

9252번: LCS 2

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

www.acmicpc.net

 

 

풀이

9251번 LCS의 확장 버전이다. LCS의 길이를 구하는 풀이는 아래 링크를 참고하길 바란다.

 https://khyunx.tistory.com/57

 

[백준 / BOJ] C++ 9251 LCS

9251번: LCS 문제 https://www.acmicpc.net/problem/9251 9251번: LCS LCS(Longest Common Subsequence, 최장 공통 부분 수열)문제는 두 수열이 주어졌을 때, 모두의 부분 수열이 되는 수열 중 가장 긴 것을 찾는 문제이다.

khyunx.tistory.com

 

위 문제에서 추가된 점은 LCS 자체를 구해야 한다. 길이를 구하는 코드에서 길이가 아닌 문자열로 바꿔주었고 문자열의 길이를 비교해 가며 같은 방식을 진행했다. 이렇게 하니 시간초과를 받았다. 

 

이유는 두 문자열의 길이가 N이라고 했을 때, 시간 복잡도가 O(N^2)일 줄 알았는데 문자열로 하게 되면 문자열끼리 비교할 때 또 시간이 걸려 O(N^3)이 되기 때문이다.

 

아래는 시간초과 코드다.

#include <bits/stdc++.h>
using namespace std;

int main() {
  ios::sync_with_stdio(false);
  cin.tie(NULL);
  string a, b;
  cin >> a >> b;
  int a_length = a.length();
  int b_length = b.length();
  vector<vector<string>> dp(a_length + 1, vector<string>(b_length + 1, ""));
  for (int i = 1; i <= a_length; i++)
    for (int j = 1; j <= b_length; j++)
      if (a[i - 1] == b[j - 1]) {
        dp[i][j] = dp[i - 1][j - 1] + a[i - 1];
      } else {
        if (dp[i - 1][j].length() > dp[i][j - 1].length())
          dp[i][j] = dp[i - 1][j];
        else
          dp[i][j] = dp[i][j - 1];
      }
  cout << dp[a_length][b_length].length() << '\n'
       << dp[a_length][b_length];
  return 0;
}

 

정답 AC를 받은 풀이는 9251번 문제와 같이 한 다음 가장 오른쪽 아래부터 역추적을 통해 정답을 찾아내는 방법이다. 표와 함께 설명하겠다.

  0 A C A Y K P
0 0 0 0 0 0 0 0
C 0 0 1 1 1 1 1
A 0 1 1 2 2 2 2
P 0 1 1 2 2 2 3
C 0 1 2 2 2 2 3
A 0 1 2 3 3 3 3
K 0 1 2 3 3 4 4

가장 오른쪽 아래에서 LCS의 길이가 4임을 알 수 있다. 여기서 왼쪽과 위쪽을 비교해 LCS의 길이가 같은지 비교한다.

  1. 만약 같으면, 그쪽으로 이동한다. (왼, 위 모두 같을 시 왼쪽 우선)
  2. 둘 다 다르면, 그 위치의 문자가 가장 LCS의 한 글자며 왼쪽 대각선 위로 이동한다.

위 방법을 거친 경로를 빨간색으로 표시하면 아래와 같다.

  0 A C A Y K P
0 0 0 0 0 0 0 0
C 0 0 1 1 1 1 1
A 0 1 1 2 2 2 2
P 0 1 1 2 2 2 3
C 0 1 2 2 2 2 3
A 0 1 2 3 3 3 3
K 0 1 2 3 3 4 4

두꺼운 부분은 그 위치의 문자가 LCS를 이룬다.

 

 

코드

#include <bits/stdc++.h>
using namespace std;

int main() {
  ios::sync_with_stdio(false);
  cin.tie(NULL);
  string a, b;
  cin >> a >> b;
  int a_size = a.size();
  int b_size = b.size();
  vector<vector<int>> dp(a_size + 1, vector<int>(b_size + 1, 0));
  // LCS 길이 구하기
  for (int i = 1; i <= a_size; i++)
    for (int j = 1; j <= b_size; j++)
      if (a[i - 1] == b[j - 1])
        dp[i][j] = dp[i - 1][j - 1] + 1;
      else
        dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);
  int len = dp[a_size][b_size];
  cout << len << '\n';
  // LCS 구하기
  vector<char> LCS(len, ' ');
  int x = a_size, y = b_size; // dp 배열에서 탐색을 시작할 좌표
  while (len > 0) { // 역추적하며 모든 문자를 찾을 때까지
    if (dp[x - 1][y] == len) { // 왼쪽이 같은 길이일 때 x좌표 이동
      x--;
    } else if (dp[x][y - 1] == len) { // 왼쪽은 같은 길이가 아니면서 위쪽이 같을 때 y좌표 이동
      y--;
    } else { // 둘 다 아닐 때 그 위치의 문자를 배열에 추가, 대각선으로 이동
      LCS[len - 1] = a[x - 1];
      x--;
      y--;
      len--;
    }
  }
  for (char i : LCS)
    cout << i;
  return 0;
}

 

728x90
반응형

'Problem Solving > BOJ' 카테고리의 다른 글

[백준 / BOJ] C++ 9213 꽤 좋은 수  (0) 2023.02.15
[백준 / BOJ] C++ 1958 LCS 3  (0) 2023.02.15
[백준 / BOJ] C++ 9251 LCS  (0) 2023.02.15
[백준 / BOJ] C++ 1269 대칭 차집합  (0) 2023.02.14
[백준 / BOJ] C++ 1267 핸드폰 요금  (0) 2023.02.14