문제

LCS(Longest Common Subsequence, 최장 공통 부분 수열)문제는 두 수열이 주어졌을 때, 모두의 부분 수열이 되는 수열 중 가장 긴 것을 찾는 문제이다.

예를 들어, ACAYKP와 CAPCAK의 LCS는 ACAK가 된다.

입력

첫째 줄과 둘째 줄에 두 문자열이 주어진다. 문자열은 알파벳 대문자로만 이루어져 있으며, 최대 1000글자로 이루어져 있다.

출력

첫째 줄에 입력으로 주어진 두 문자열의 LCS의 길이를, 둘째 줄에 LCS를 출력한다.

LCS가 여러 가지인 경우에는 아무거나 출력하고, LCS의 길이가 0인 경우에는 둘째 줄을 출력하지 않는다.

내가 푼 풀이

- 이전에 LCS를 dp를 이용하여 풀었던 기억을 떠올리며, 코드를 구현했다.

- LCS는 순서에따라 존재하는 같은 알파벳의 중 가장 긴 수열을 출력하면 된다.

- 위에 주어진 ACAYKP와 CAPCAK 에서는 순서만 고려하였을 때  ACAYKP와 CAPCAK 로 ACAK가 되는 것이다.

- 이는 이차원배열을 이용하여 순서대로 탐색했을 때, 같은 알파벳을 만난 경우, 이전 알파벳들 중 같은 알파벳을 만난 수열의 길이를 가져와 + 1을 해주면 된다.

- 다른 알파벳을 만났다면, 이전 알파벳 중 LCS가 가장 긴 수열의 길이를 넣어준다.

    A C A Y K P
  0 0 0 0 0 0 0
C 0 0 1 1 1 1 1
A 0 1 1 2 2 2 2

위 표대로 ACAYKP 수열에서 CAPCAK의 수열 중 알파벳 하나씩 가져와 순서대로 탐색한다.

- 첫 번째 알파벳 C를 검사했을 때, 두 번째에 있는 C와 일치하므로, ACAYKP와 C의 LCS는 C이고, 길이는 1이다.

- 이후의 알파벳은 같은 수를 만나지 않았으므로 이전의 가장 긴 수열의 길이를 가져온다.

- 두 번째 알파벳 A를 검사했을 때, 첫 번째 A와 같은 알파벳이고, 세 번째에도 A가 나온다.

- 첫번째 A는 A수열과 CA수열의 LCS이고, 세번째 A는 ACA와 CA의 LCS이다. 

- LCS: 첫 번째는 A, 세 번째는 CA가 된다.

- 동일한 알파벳을 만난다면 x-1 y-1에 위치한 lcs의 길이 +1을 해준다.

- 동일하지 않은 알파벳을 만난다면 x-1, y와 x, y-1 중 LCS길이가 더 긴 수를 가져온다.

- x-1, y-1에서 길이를 가져오는 이유는 위 표에서 x-1, y-1이 의미하는 것은 현재 검사하는 알파벳이전까지 검사했던 LCS의 최대길이 이다.

- LCS는 순서만 따졌을 때 존재하는 같은 알파벳의 수열의 최대길이가 되므로, x-1, y-1에서 가져온다.

 

길이는 다음과 같이 구하고, LCS의 수열은 역순으로 오른쪽 아래부터 x, y의 값이 x-1, y와 x, y-1의 값과 다른 경우 문자를 추가하는 방법을 사용했다.

위의 표가 만들어지는 과정의 역순으로 이용하였다.

 

코드로 구현하면 다음과 같다.

 

 

import Foundation

// 입력
var s1 = Array(readLine()!).map{ String($0) }
var s2 = Array(readLine()!).map{ String($0) }
var dp = Array(repeating: Array(repeating: 0, count: s1.count+1), count: s2.count+1)
s1.insert("0", at: 0)
s2.insert("0", at: 0)


// DP
// 같은 알파벳을 마주한경우, 이전까지 탐색했던 LCS의 최대길이 + 1을 해준다.
// 다른알파벳을 만났다면 이전에 검사했던 LCS중 가장 긴 길이를 가져온다.
for i in 1..<s2.count {
    for j in 1..<s1.count {
        if s2[i] == s1[j] {
            dp[i][j] = dp[i-1][j-1] + 1
        } else {
            dp[i][j] = max(dp[i-1][j], dp[i][j-1])
        }
    }
}

// LCS의 수열 구하기.
// 위의 DP표를 구했던 방법의 역순으로 구하였다.
var idx = dp[s2.count-1][s1.count-1]
var x = s1.count - 1
var y = s2.count - 1
var result = ""
while idx != 0 {
    if dp[y][x] == dp[y-1][x] {
        y -= 1
        continue
    }
    if dp[y][x] == dp[y][x-1] {
        x -= 1
        continue
    }
    result = s1[x] + result
    x -= 1
    y -= 1
    idx -= 1
}
print(result.count)
print(result)

'코딩테스트 > 백준' 카테고리의 다른 글

BOJ-28278 스택 2 Swift  (0) 2024.04.02
BOJ-2012 등수 매기기 Swift  (0) 2023.07.10
BOJ-11403 경로 찾기 Swift  (0) 2023.07.01
BOJ-1417 국회의원 선거 Swift  (0) 2023.07.01
BOJ-10942 팰린드롬? Swift  (0) 2023.07.01

+ Recent posts