일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- edge detection
- 딥러닝
- 백준
- TD
- 자료구조
- clustering
- 머신러닝
- Mask Processing
- MinHeap
- DP
- exists
- SIFT
- dfs
- IN
- dynamic programming
- classification
- BFS
- canny edge detection
- 강화학습
- machine learning
- sklearn
- MySQL
- 인공지능
- 그래프 이론
- opencv
- Python
- image processing
- C++
- AlexNet
- Reinforcement Learning
- Today
- Total
JINWOOJUNG
[ DP - 9251 ] LCS(C++) 본문
접근법
LCS(Longest Common Subsequence, 최장 공통 부분 수열)문제는 두 수열이 주어졌을 때, 모두의 부분 수열이 되는 수열 중 가장 긴 것을 찾는 문제이다. 여기서 부분 열은 상대적인 순서를 유지해야 하는데, 예를들어 ACAYKP의 부분 수열은 ACA, ACP, CYK, CAP 등 순서를 유지한 부분 수열이다.
DP의 대표적인 문제 중 하나로, 스트링 편집 거리와 유사하게 접근해 볼 수 있다. 위에서 주어진 예시를 이용하면 "ACAYKP", "CAPCAK"의 LCS를 찾아야 한다. 스트링 편집 거리와 같이 한 문자열은 고정시켜 두고, 나머지 하나의 문자열 중 하나씩 증가시키면서 비교 해 보자.
A | C | A | Y | K | P | |
C | 0 |
"C"와 "A"의 LCS는 없다. 따라서 0이다.
A | C | A | Y | K | P | |
C | 0 | 1 |
"C"와 "AC"의 LCS는 "C"이다. 따라서 1이다.
A | C | A | Y | K | P | |
C | 0 | 1 | 1 |
"C"와 "ACA"의 LCS는 "C"이다. 마지막 문자가 달라도, 가장 긴 부분 수열이기 때문에, 이전의 최댓값인 1("C")를 그대로 가져와야 한다.
A | C | A | Y | K | P | |
C | 0 | 1 | 1 | 1 | 1 | 1 |
따라서 마지막 까지 진행하면 다음과 같다.
A | C | A | Y | K | P | |
C | 0 | 1 | 1 | 1 | 1 | 1 |
A | 1 |
여기서 부터 잘 생각 해 보자. 우리는 DP로 해당 문제를 접근할 것이므로, Recursive Property를 찾아야 한다. "CA"와 "A"의 LCS는 마지막 문자가 동일하므로 1("A")이다. 아직까지는 찾지 못하였다.
A | C | A | Y | K | P | |
C | 0 | 1 | 1 | 1 | 1 | 1 |
A | 1 | 1 |
"CA"와 "AC"의 LCS는 1("A")이다. 그렇다면 단순히 마지막 문자가 다른 경우 이전까지의 최댓값 즉, DP[i][j-1]만 고려하면 될까? 아니다. 두 문자열에 공통적인 부분수열이기에, "CA"와 "A"의 LCS와 "C"와 "AC"의 부분수열 모두를 고려하여 최댓값을 가져와야 한다. 이 경우 둘다 1이기에 DP[i][j]는 1이 된다.
$$ DP[i][j] = max(DP[i][j-1] , DP[i-1][j]) \quad , \quad if \; S[i] != S2[j]$$
A | C | A | Y | K | P | |
C | 0 | 1 | 1 | 1 | 1 | 1 |
A | 1 | 1 | 2 |
"CA"와 "ACA"의 LCS는 2("CA")이다. 이 경우 마지막 문자가 동일한 경우이다. 마지막 문자가 동일한 경우 무조건 +1을 해 줘야 하기 때문에 두 문자열의 마지막 문자를 제외한 최댓값을 가져와야 한다. 따라서 DP[i-1][j-1]에 1을 더해야 정확한 LCS를 계산할 수 있다. 이를 위해서 DP의 첫번째 행과 열은 모두 0이 되어야 한다.
$$ DP[i][j] = 0 \quad , \quad if \; \, i == 0 \; or \; j==0$$
$$ DP[i][j] = DP[i-1][j-1] \, + \, 1 \quad , \quad if \; S[i] == S2[j]$$
따라서 위 방법으로 LCS를 구하면 다음과 같다.
A | C | A | Y | K | P | |
C | 0 | 1 | 1 | 1 | 1 | 1 |
A | 1 | 1 | 2 | 2 | 2 | 2 |
P | 1 | 1 | 2 | 2 | 2 | 3 |
C | 1 | 2 | 2 | 2 | 2 | 3 |
A | 1 | 2 | 3 | 3 | 3 | 3 |
K | 1 | 2 | 3 | 3 | 4 | 4 |
정답
#include <iostream>
#include <vector>
using namespace std;
string s_S1, s_S2;
int32_t ars32_DP[1001][1001];
int32_t main()
{
int32_t s32_I, s32_J;
cin >> s_S1 >> s_S2;
// DP 테이블 구성 시 문자열의 처음 값을 0으로 설정하기 위함
s_S1 = "0" + s_S1;
s_S2 = "0" + s_S2;
for (s32_I = 0; s32_I < s_S1.size(); s32_I++)
{
for (s32_J = 0; s32_J < s_S2.size(); s32_J++)
{
if (s32_I == 0 || s32_J == 0)
{
ars32_DP[s32_I][s32_J] = 0;
}
// 두 문자열의 마지막이 동일한 문자인 경우
else if (s_S1[s32_I] == s_S2[s32_J])
{
ars32_DP[s32_I][s32_J] = ars32_DP[s32_I - 1][s32_J - 1] + 1;
}
// 두 문자열의 마지막이 동일하지 않은 경우
else
{
ars32_DP[s32_I][s32_J] = max(ars32_DP[s32_I - 1][s32_J], ars32_DP[s32_I][s32_J - 1]);
}
}
}
cout << ars32_DP[s32_I - 1][s32_J - 1] << endl;
return 0;
}
'백준' 카테고리의 다른 글
[ DP - 11053 ] 가장 긴 증가하는 부분 수열(C++) (3) | 2024.06.18 |
---|---|
[ DFS/BFS - 7576 ] 토마토(C++/BFS) (0) | 2024.05.29 |
[ DFS/BFS - 2606 ] 바이러스(C++/BFS) (0) | 2024.05.25 |
[ DP ] Dijkstra Algorithm (0) | 2024.05.25 |
[ DP ] 조약돌 놓기(C++) (0) | 2024.05.24 |