9252번: LCS 2
LCS(Longest Common Subsequence, 최장 공통 부분 수열)문제는 두 수열이 주어졌을 때, 모두의 부분 수열이 되는 수열 중 가장 긴 것을 찾는 문제이다. 예를 들어, ACAYKP와 CAPCAK의 LCS는 ACAK가 된다.
www.acmicpc.net
문제 읽기
LCS, 즉 최장 공통 부분 수열의 길이를 찾으면서, 동시에 수열까지 구해야 하는 문제이다. 이전에 한 번 풀어봤지만 잘 기억이 나지 않아 다시 공부하면서 풀어보았다. 한 마디로 DP를 활용하면 된다.
문제 풀기
LCS 수열 길이 구하기
일단 점화식을 찾기 위해 그려봤다.
이런 식으로 구할 수 있다.
즉, 두 문자열에서 str1의 i번째 문자, str2의 j번째 문자가 같은지, 같지 않은 지에 따라서 점화식을 세워볼 수 있다.
점화식은 다음과 같다.
간단히 설명하자면, 각 문자가 같으면 왼쪽 위 대각선 값에 +1을 해주고, 각 문자가 다르면 str1의 i번째 문자만 포함했을 때의 최댓값, str2의 j번째 문자만 포함했을 때의 최댓값 중 큰 값을 넣어주면 된다.
또한 dp[i][0]과 dp[0][j] 인 각 첫 번째 행렬은 따로 초기화를 시켜주어야 한다.
boolean flag = false;
for (int i = 0; i < N; i++){
if (str2[0] == str1[i]) flag = true;
if (flag) dp[i][0] = 1;
}
flag = false;
for (int j = 0; j < M; j++){
if (str1[0] == str2[j]) flag = true;
if (flag) dp[0][j] = 1;
}
이런 식으로 DP를 조금만 활용하면 되기 때문에 LCS의 수열 길이를 찾는 것은 크게 어렵지 않다.
해당 부분 코드는 다음과 같다.
for (int i = 1; i < N; i++){
for (int j = 1; j < M; j++){
if (str1[i] == str2[j]) dp[i][j] = dp[i-1][j-1]+1;
else dp[i][j] = Math.max(dp[i-1][j], dp[i][j-1]);
}
}
하지만 해당 문제에서는 수열도 출력하길 요구한다.
LCS 수열 구하기
이건 계속 해봐도 잘 모르겠어서 찾아봤다! 아래 블로그에 설명이 잘 되어있었다.
[알고리즘] 그림으로 알아보는 LCS 알고리즘 - Longest Common Substring와 Longest Common Subsequence
LCS는 주로 최장 공통 부분수열(Longest Common Subsequence)을 말합니다만, 최장 공통 문자열(Longest Common Substring)을 말하기도 합니다.
velog.io
위 그림의 파란색 화살표에 주목해보자.
N-1, M-1 좌표에서부터 시작해서 다음과 같은 과정을 통해 수열을 구할 수 있다.
- 맨 오른쪽 아래인 N-1, M-1 좌표에서 시작한다.
- 현 좌표에서 위쪽과 왼쪽 좌표를 확인한다
- 현 위치와 같은 값이면 이동한다.
- 현 위치와 다른 값이면 왼쪽 위 대각선으로 이동한다.
- 2번을 계속 반복하면서 0을 만나면 끝낸다.
해당 부분 코드는 다음과 같다. queue를 사용하긴 했는데 굳이 안 써도 될 것 같다.
//lcs 수열 구하기
Queue<Pos> q = new ArrayDeque<>();
q.offer(new Pos(N-1, M-1));
int[] dr = {-1, 0};
int[] dc = {0, -1}; //상, 좌
while(!q.isEmpty()){
Pos cur = q.poll();
boolean canGo = false;
for (int di = 0; di < 2; di++){
int nr = cur.r + dr[di];
int nc = cur.c + dc[di];
if (nr < 0 || nr >= N || nc < 0 || nc >= M) continue;
if (dp[cur.r][cur.c] == dp[nr][nc]){
canGo = true;
q.offer(new Pos(nr, nc));
break; //괜찮겠지?
}
}
if (!canGo) {
sb.append(str1[cur.r]);
if (dp[cur.r][cur.c] == 1) break;
q.offer(new Pos(cur.r-1, cur.c-1));
}
}
}
코드
package ps.ㄱSolving;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayDeque;
import java.util.Arrays;
import java.util.Queue;
public class BOJ_G4_9252_LCS2 {
static class Pos{
int r, c;
public Pos(int r, int c){
this.r = r;
this.c = c;
}
}
public static void main(String[] args) throws IOException {
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
char[] str1 = in.readLine().toCharArray();
char[] str2 = in.readLine().toCharArray();
int N = str1.length;
int M = str2.length;
int[][] dp = new int[N][M];
boolean flag = false;
for (int i = 0; i < N; i++){
if (str2[0] == str1[i]) flag = true;
if (flag) dp[i][0] = 1;
}
flag = false;
for (int j = 0; j < M; j++){
if (str1[0] == str2[j]) flag = true;
if (flag) dp[0][j] = 1;
}
for (int i = 1; i < N; i++){
for (int j = 1; j < M; j++){
if (str1[i] == str2[j]) dp[i][j] = dp[i-1][j-1]+1;
else dp[i][j] = Math.max(dp[i-1][j], dp[i][j-1]);
}
}
// for (int i = 0; i < N; i++){
// System.out.println(Arrays.toString(dp[i]));
// }
StringBuilder sb = new StringBuilder();
int len = dp[N-1][M-1];
if (len == 0){
System.out.println(len);
return;
}
//lcs 수열 구하기
Queue<Pos> q = new ArrayDeque<>();
q.offer(new Pos(N-1, M-1));
int[] dr = {-1, 0};
int[] dc = {0, -1}; //상, 좌
while(!q.isEmpty()){
Pos cur = q.poll();
boolean canGo = false;
for (int di = 0; di < 2; di++){
int nr = cur.r + dr[di];
int nc = cur.c + dc[di];
if (nr < 0 || nr >= N || nc < 0 || nc >= M) continue;
if (dp[cur.r][cur.c] == dp[nr][nc]){
canGo = true;
q.offer(new Pos(nr, nc));
break; //괜찮겠지?
}
}
if (!canGo) {
sb.append(str1[cur.r]);
if (dp[cur.r][cur.c] == 1) break;
q.offer(new Pos(cur.r-1, cur.c-1));
}
}
System.out.println(len);
System.out.println(sb.reverse());
}
}
'Algorithm > Dynamic Programming' 카테고리의 다른 글
BOJ G4 14002 가장 긴 증가하는 부분수열4 JAVA (0) | 2024.03.15 |
---|---|
BOJ G3 2482 색상환 JAVA (0) | 2024.03.12 |
BOJ G4 17404 RGB 거리 2 (1) | 2024.02.29 |
BOJ G3 2186 문자판 JAVA (0) | 2024.02.28 |
BOJ G4 10942 팰린드롬? JAVA (0) | 2024.02.02 |