시간 제한 | 메모리 제한 | 제출 | 정답 | 맞힌 사람 | 정답 비율 |
0.1 초 (하단 참고) | 256 MB | 62233 | 25245 | 18538 | 40.216% |
문제
LCS(Longest Common Subsequence, 최장 공통 부분 수열)문제는 두 수열이 주어졌을 때, 모두의 부분 수열이 되는 수열 중 가장 긴 것을 찾는 문제이다.
예를 들어, ACAYKP와 CAPCAK의 LCS는 ACAK가 된다.
입력
첫째 줄과 둘째 줄에 두 문자열이 주어진다. 문자열은 알파벳 대문자로만 이루어져 있으며, 최대 1000글자로 이루어져 있다.
출력
첫째 줄에 입력으로 주어진 두 문자열의 LCS의 길이를 출력한다.
예제 입력 1
ACAYKP CAPCAK
예제 출력 1
4
알고리즘 분류
- 다이나믹 프로그래밍
- 문자열
시간 제한
- Java 8: 0.4 초
- Python 3: 2 초
- PyPy3: 2 초
- Java 8 (OpenJDK): 0.4 초
- Java 11: 0.4 초
- Kotlin (JVM): 0.4 초
- Java 15: 0.4 초
문제 출처
https://www.acmicpc.net/problem/9251
9251번: LCS
LCS(Longest Common Subsequence, 최장 공통 부분 수열)문제는 두 수열이 주어졌을 때, 모두의 부분 수열이 되는 수열 중 가장 긴 것을 찾는 문제이다. 예를 들어, ACAYKP와 CAPCAK의 LCS는 ACAK가 된다.
www.acmicpc.net
문제 해결 방법
- 가능한 수열을 하나하나씩 비교하면 시간 초과가 발생하므로, DP를 이용하여 풀어야 하는 문제이다.
- 비교해야 할 문자열이 2개 이므로(
A
,B
), 2차원 DP 테이블을 생성해준다.
int dp[a][b];
- 2차원 DP 테이블의 각 값의 의미는 현재 행(
a
)까지의 문자열과 현재 열(b
)까지의 문자열 사이의 LCS 길이이다.
예제를 이용하여 문제 해결 방법 알아보기
- 문제에서 주어진
입력 예제 1
의 값(A
,B
)을 2차원 표(dp
)를 이용하여 풀어보면 다음과 같다.
①dp[0][x]
(0 ≤x
≤ 6)의 경우
- LCS를 구할 수 없으므로 모두
0
으로 채워준다.
B \ A | 0 | A | C | A | Y | K | P |
0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
C | |||||||
A | |||||||
P | |||||||
C | |||||||
A | |||||||
K |
②dp[1][x]
(0 ≤x
≤ 6)의 경우
- 문자열
B
의{ C }
와 문자열A
의{ A, AC, ACA, ACAY, ACAYK, ACAYKP }
의 LCS를 구하면 다음과 같다.- C와 A : 0
- C와 AC : C
- C와 ACA : C
- C와 ACAY : C
- C와 ACAYK : C
- C와 ACAYKP : C
B \ A | 0 | A | C | A | Y | K | P |
0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
C | 0 | 0 | 1 | 1 | 1 | 1 | 1 |
A | |||||||
P | |||||||
C | |||||||
A | |||||||
K |
③dp[2][x]
(0 ≤x
≤ 6)의 경우
- 문자열
B
의{ CA }
와 문자열A
의{ A, AC, ACA, ACAY, ACAYK, ACAYKP }
의 LCS를 구하면 다음과 같다.- CA와 A : A
- CA와 AC : A 또는 C
- CA와 ACA : CA
- CA와 ACAY : CA
- CA와 ACAYK : CA
- CA와 ACAYKP : CA
B \ A | 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 | |||||||
C | |||||||
A | |||||||
K |
④dp[3][x]
(0 ≤x
≤ 6)의 경우
- 문자열
B
의{ CAP }
와 문자열A
의{ A, AC, ACA, ACAY, ACAYK, ACAYKP }
의 LCS를 구하면 다음과 같다.- CAP와 A : A
- CAP와 AC : A 또는 C
- CAP와 ACA : CA
- CAP와 ACAY : CA
- CAP와 ACAYK : CA
- CAP와 ACAYKP : CAP
B \ A | 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 | |||||||
A | |||||||
K |
⑤dp[4][x]
(0 ≤x
≤ 6)의 경우
- 문자열
B
의{ CAPC }
와 문자열A
의{ A, AC, ACA, ACAY, ACAYK, ACAYKP }
의 LCS를 구하면 다음과 같다.- CAPC와 A : A
- CAPC와 AC : AC
- CAPC와 ACA : CA
- CAPC와 ACAY : CA
- CAPC와 ACAYK : CA
- CAPC와 ACAYKP : CAP
B \ A | 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 | |||||||
K |
⑥dp[5][x]
(0 ≤x
≤ 6)의 경우
- 문자열
B
의{ CAPCA }
와 문자열A
의{ A, AC, ACA, ACAY, ACAYK, ACAYKP }
의 LCS를 구하면 다음과 같다.- CAPCA와 A : A
- CAPCA와 AC : AC
- CAPCA와 ACA : ACA
- CAPCA와 ACAY : ACA
- CAPCA와 ACAYK : ACA
- CAPCA와 ACAYKP : CAP 또는 ACA
B \ A | 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 |
⑦dp[6][x]
(0 ≤x
≤ 6)의 경우
- 문자열
B
의{ CAPCAK }
와 문자열A
의{ A, AC, ACA, ACAY, ACAYK, ACAYKP }
의 LCS를 구하면 다음과 같다.- CAPCAK와 A : A
- CAPCAK와 AC : AC
- CAPCAK와 ACA : ACA
- CAPCAK와 ACAY : ACA
- CAPCAK와 ACAYK : ACAK
- CAPCAK와 ACAYKP : ACAK
B \ A | 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 |
규칙 발견하기
- 위의 과정을 통해 다음의 2가지 규칙을 발견할 수 있다.
① 해당 칸의 가로 축과 세로 축의 문자가 같을 경우
- 해당 칸의 왼쪽 대각선 값에 1을 더해준 값으로 업데이트 해준다.
B \ A | 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 |
- 이것을 코드로 표현하면 다음과 같다.
if (a[i - 1] == b[j - 1]) { dp[i][j] = dp[i - 1][j - 1] + 1; // 왼쪽 대각선에 있는 값 + 1로 업데이트 }
② 해당 칸의 가로 축과 세로 축의 문자가 다를 경우
- 해당 칸의 왼쪽과 위의 값 중에서 큰 값으로 업데이트 해준다.
B \ A | 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 |
- 이것을 코드로 표현하면 다음과 같다.
else { // if (a[i - 1] != b[j - 1]) { dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]); // 해당 칸의 왼쪽 값에 있는 값과 위에 있는 값 중 큰 값으로 업데이트 }
- 따라서 문제 해결을 위한 코드는 다음과 같다.
int Solution(string a, string b) { int lenA, lenB; lenA = a.size(); lenB = b.size(); for (int i = 1; i <= lenA; i++) { for (int j = 1; j <= lenB; 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]); } } } return dp[lenA][lenB]; }
코드
#include <iostream> #include <algorithm> #include <string> using namespace std; string A, B; int dp[1001][1001]; void Input() { cin >> A >> B; } int Solution(string a, string b) { int lenA, lenB; lenA = a.size(); lenB = b.size(); for (int i = 1; i <= lenA; i++) { for (int j = 1; j <= lenB; 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]); } } } return dp[lenA][lenB]; } void Output() { cout << Solution(A, B) << '\n'; } void Solve() { Input(); Output(); } int main() { ios_base::sync_with_stdio(false); cin.tie(NULL); cout.tie(NULL); Solve(); return 0; }
채점 결과

참고
- [단계별로 풀어보기] > [동적 계획법 1]
- 골드V
최장 공통 부분 수열(LCS; Longest Common Subsequence)
개념
- 동적 계획법(DP)으로 풀 수 있는 유명한 알고리즘 문제
- 주어진 여러 개의 수열 모두의 부분 수열이 되는 수열들 중에 가장 긴 것을 찾는 문제
- 컴퓨터 과학에서 고전으로 통하는 문제이며, 생물 정보학에서도 많이 응용되고 있는 문제이다.
- 연속되어 있는 공통 문자열을 찾는 최장 공통 부분 문자열(Longest Common Substring) 문제와 혼동해서는 안 된다.
점화식
알고리즘()
int LCS(string a, string b) { int n, m; n = a.size(); m = b.size(); for (int i = 1; i <= n; i++) { for (int j = 1; j <= m; 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]); } } } return dp[n][m]; }
참고 사이트
최장 공통 부분 수열 - 위키백과, 우리 모두의 백과사전
위키백과, 우리 모두의 백과사전. 최장 공통 부분수열 문제는 LCS라고도 불린다. 이는 주어진 여러 개의 수열 모두의 부분수열이 되는 수열들 중에 가장 긴 것을 찾는 문제다.(종종 단 두 개중 하
ko.wikipedia.org
'Problem Solving > BOJ' 카테고리의 다른 글
[BOJ-11660][C++] 구간 합 구하기 5 (2) | 2023.01.24 |
---|---|
[BOJ-10986][C++] 나머지 합 (2) | 2023.01.18 |
[BOJ-16139][C++] 인간-컴퓨터 상호작용 (0) | 2023.01.10 |
[BOJ-12865][C++] 평범한 배낭 (0) | 2023.01.09 |
[BOJ-2565][C++] 전깃줄 (0) | 2023.01.04 |
[BOJ-11055][C++] 가장 큰 증가 부분 수열 (0) | 2022.12.14 |
[BOJ-11054][C++] 가장 긴 바이토닉 부분 수열 (0) | 2022.12.14 |
[BOJ-11722][C++] 가장 긴 감소하는 부분 수열 (0) | 2022.12.14 |