문제 출처 : www.acmicpc.net/problem/9251
문제
LCS(Longest Common Subsequence, 최장 공통 부분 수열)문제는 두 수열이 주어졌을 때, 모두의 부분 수열이 되는 수열 중 가장 긴 것을 찾는 문제이다.
예를 들어, ACAYKP와 CAPCAK의 LCS는 ACAK가 된다.
입력
첫째 줄과 둘째 줄에 두 문자열이 주어진다. 문자열은 알파벳 대문자로만 이루어져 있으며, 최대 1000글자로 이루어져 있다.
출력
첫째 줄에 입력으로 주어진 두 문자열의 LCS의 길이를 출력한다.
알고리즘 분류
풀이
입력된 두 개의 문자열을 비교하면서 LCS(최장 공통 부분 수열)을 찾는 문제로, 가능한 수열을 하나하나 비교하면
시간 초과이기 때문에, dp를 이용하는 문제이다.
입력 받은 ACAYKP, CAPCAK로 표를 채워가면서 점화식을 유추해보자.
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 | ||||||
P | 0 | ||||||
C | 0 | ||||||
A | 0 | ||||||
K | 0 |
해당 표에 들어가는 값의 의미는, 현재 행까지의 문자열과, 현재 열까지의 문자열 사이의 LCS길이이다.
우선 문자열과 0을 비교했을 때는 당연히 0으로 다 채워주고(0행,0열),
1행(C)부터 채워보자.
C와 A LCS : 0
C와 AC LCS : C
C와 ACA LCS : C
C와 ACAY LCS : C
C와 ACAYK LCS : C
C와 ACAYKP LCS : C
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 | ||||||
C | 0 | ||||||
A | 0 | ||||||
K | 0 |
2행(CA)
CA와 A LCS : A
CA와 AC LCS : A OR C
CA와 ACA LCS : CA
CA와 ACAY LCS: CA
CA와 ACAYK LCS: CA
CA와 ACAYKP LCS: CA
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 | ||||||
A | 0 | ||||||
K | 0 |
3행(CAP)
CAP와 A LCS : A
CAP와 AC LCS : A OR C
CAP와 ACA LCS : CA
CAP와 ACAY LCS: CA
CAP와 ACAYK LCS: CA
CAP와 ACAYKP LCS: CAP
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 |
6행(CAPCAK)
CAPCAK와 A LCS : A
CAPCAK와 AC LCS : AC
CAPCAK와 ACA LCS : ACA
CAPCAK와 ACAY LCS: ACA
CAPCAK와 ACAYK LCS: ACAK
CAPCAK와 ACAYKP LCS: ACAK
이렇게 채운 표의 규칙을 살펴보면, 두 가지의 규칙이 나온다.
1.해당 칸의 행과 열의 문자가 같을 때는 해당 칸의 왼쪽 대각선의 값에 +1,
2.해당 칸의 행과 열의 문자가 다를 때는 해당 칸의 왼쪽, 위의 값 중 큰 값을 가져온다.
이를 점화식으로 세우면
1. dp[i][j] = dp[i-1][j-1]+1;
2. dp[i][j] = max(dp[i-1][j], dp[i][j-1]);
위 식을 가지고 dp를 채워나가고, dp[문자열 a의 길이][문자열 b의 길이]를 출력하면 된다.
코드
#include<iostream>
#include<string>
#include<algorithm>
using namespace std;
int dp[1001][1001];
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
string a, b;
cin >> a >> b;
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] + 1;
}
else {
dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);
}
}
}
cout << dp[a.length()][b.length()];
return 0;
}
'알고리즘 문제 풀이 > 백준' 카테고리의 다른 글
백준 12865 평범한 배낭 c++ (0) | 2021.04.29 |
---|---|
백준 1912 연속합 c++ (0) | 2021.04.29 |
백준 2565 전깃줄 c++ (2) | 2021.04.22 |
백준 11054 가장 긴 바이토닉 부분 수열 c++ (dp) (0) | 2021.04.20 |
백준 11053 가장 긴 증가하는 부분 수열 c++,Kotlin (dp) (0) | 2021.04.19 |
댓글