일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- 20115
- Java
- join제거
- 무한페이징
- 이분 탐색
- 백준
- 이진 탐색
- EntityGraph
- 로그
- NCP
- 그리디
- 2512
- 1495
- 13975
- slice개념
- 19598
- binary search
- greedy
- 12738
- 14921
- Upper bound
- 모니터링
- DP
- no offset
- dto projection
- Blue/Green
- Lower bound
- 11501
- 3187
- Promtail
- Today
- Total
멘지의 기록장
[백준] 9252 : LCS 2 (JAVA) 본문
난이도
🥇 4
링크
https://www.acmicpc.net/problem/9252
9252번: LCS 2
LCS(Longest Common Subsequence, 최장 공통 부분 수열)문제는 두 수열이 주어졌을 때, 모두의 부분 수열이 되는 수열 중 가장 긴 것을 찾는 문제이다. 예를 들어, ACAYKP와 CAPCAK의 LCS는 ACAK가 된다.
www.acmicpc.net
문제
풀이과정
해당 문제는 최장 공통 부분수열 (LCS)를 사용하는 문제이다.
LCS란 Longest Common Subsequence의 약자로, 최장 공통 문자열 (Longest Common Substring)을 말하기도 한다.
문자열 ABCDEF 와 GBCDFE로 차이점을 알아보자면
최장 공통 부분수열 : ABCDEF 와 GBCDFE ➡️ BCDF / ABCDEF 와 GBCDFE ➡️ BCDE 2가지가 될 수 있고,
최장 공통 문자열 : ABCDEF와GBCDFE ➡️BCD 가 될 수 있다.
즉, 최장 공통 문자열은 부분 문자열이 아니기 때문에 한번에 이어져있는 문자열만 가능하다는 것이다!
하지만, 이번 문제는 최장 공통 문자열이 아닌! 최장 공통 부분수열을 사용하여 푸는 문제였다.
우선 예제에서 주어진 두 수열은 ACAYKP와 CAPCAK 이다.
두 수열에서 LCS를 2차원 배열을 사용하여 구해보자면, 배열 안의 수는 LCS의 길이이다.
만약 두 문자가 같다면 왼쪽 위 대각선의 값에 +1을 해주고,
dp[i][j] = dp[i-1][j-1] + 1;
두 문자가 다르다면 왼쪽이나 위쪽 값 중 큰 값에 +1을 해준다.
dp[i][j] = Math.max(dp[i-1][j], dp[i][j-1]);
위의 점화식을 통해 구한 배열이다.
0 | C | A | P | C | A | K | |
0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
A | 0 | 0 | 1 | 1 | 1 | 1 | 1 |
C | 0 | 1 | 1 | 1 | 2 | 2 | 2 |
A | 0 | 1 | 2 | 2 | 2 | 3 | 3 |
Y | 0 | 1 | 2 | 2 | 2 | 3 | 3 |
K | 0 | 1 | 2 | 3 | 3 | 3 | 4 |
P | 0 | 1 | 2 | 3 | 3 | 3 | 4 |
이때 색칠된 숫자는 dp의 값이 변하는 부분인 것을 확인할 수 있다.
그렇기에 기존 LCS에서 최장 공통 부분수열을 구하려면 해당 위치들을 탐색하여 출력하면 되는 것이다.
즉, 위의 점화식을 반대로 만들면 되기에
1. 현재 값 == 위쪽 값 ➡️ 위쪽으로 이동
2. 현재 값 == 왼쪽 값 ➡️ 왼쪽으로 이동
3. 1과 2 모두 아니라면 ➡️ 왼쪽 위 대각선에서 온 값 ➡️ 두 문자 같음 ➡️ Stack에 넣기 ➡️ 왼쪽 위 대각선으로 이동
위와 같은 과정을 거친 후 Stack에 입력된 값을 pop하여 출력하면 최장 공통 부분수열인 ACAK를 구할 수 있다.
📌 Stack을 사용한 이유? 후입선출의 자료구조로 뒤에서부터 탐색하였기 때문에 문자열을 순차적으로 출력하기 위해 사용
전체 코드
import java.io.*;
import java.util.*;
public class Main {
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringBuilder sb = new StringBuilder();
String input1 = br.readLine();
String input2 = br.readLine();
int length1 = input1.length();
int length2 = input2.length();
int[][] dp = new int[length1+1][length2+1];
for (int i=1; i<=length1; i++) {
for (int j=1; j<=length2; j++) {
if (input1.charAt(i-1) == input2.charAt(j-1))
dp[i][j] = dp[i-1][j-1] + 1;
else
dp[i][j] = Math.max(dp[i-1][j], dp[i][j-1]);
}
}
sb.append(dp[length1][length2]).append("\n");
Stack<Character> stack = new Stack<>();
while (length1 > 0 && length2 > 0) {
if (dp[length1][length2] == dp[length1-1][length2]) {
length1--;
}
else if (dp[length1][length2] == dp[length1][length2-1]) {
length2--;
}
else {
stack.push(input1.charAt(length1-1));
length1--;
length2--;
}
}
while(!stack.isEmpty()) {
sb.append(stack.pop());
}
System.out.println(sb);
}
}
결과
'BOJ' 카테고리의 다른 글
[백준] 1946 : 신입 사원 (JAVA) (0) | 2024.01.08 |
---|---|
[백준] 1449 : 수리공 항승 (JAVA) (0) | 2024.01.07 |
[백준] 2512 : 예산 (JAVA) (0) | 2024.01.03 |
[백준] 2110 : 공유기 설치 (JAVA) (1) | 2024.01.02 |
[백준] 12738 : 가장 긴 증가하는 부분 수열 3 (JAVA) (1) | 2024.01.01 |