LCS(Longest Common Subsequence) 는 가장 긴 공통 부분 수열으로, 공통적으로 일치하는 수열 중 가장 긴 부분을 의미한다.
LIS(Longest Increasing Subsequence)는 가장 긴 증가(또는 감소) 하는 수열을 의미한다.
두 가지 모두 **DP 알고리즘**에 속한다.두 가지의 개념과 구현코드를 정리할 예정이다!
ex) CAPCAK, ACAYKP 의 LCS는 ACAK 가 된다.
결론적으로 LCS의 점화식은 아래와 같다.
비교하고 있는 두 문자가 같다면 ->
dp[i][j] = dp[i-1][j-1] + 1
비교하고 있는 두 문자가 다르다면 ->dp[i][j] = Math.max(dp[i-1][j], dp[i][j - 1]

(C,A) 의 경우는, C와 A를 비교하겠다는 뜻이다.dp[i][j] = Math.max(dp[i-1][j], dp[i][j - 1]의 값이 된다.
(C,C)의 경우는 C와 AC를 비교하는 것을 의미하며 비교중인 문자는 C, C를 의미한다.dp[i][j] = dp[i-1][j-1] + 1 의 값이 된다.
(C, A)의 경우, C와 ACA를 비교하는 것을 의미하며 비교중인 문자는 C, A를 의미한다.dp[i][j] = Math.max(dp[i-1][j], dp[i][j - 1]의 값이 된다.
마지막 행, 마지막 열에 위치한 숫자가 두 문자열의 LCS 값이 된다.
const readline = require('readline');
const rl = readline.createInterface({
input : process.stdin,
output : process.stdout
});
function solution (input) {
const str1 = input[0];
const str2 = input[1];
const dp = Array.from({length : input[0].length + 1}, () => Array(input[1].length + 1).fill(0));
for (let i = 1; i <= str1.length; i++) {
for (let j = 1; j <= str2.length; j++) {
if (str1[i - 1] === str2[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]);
}
}
}
console.log(dp[input[0].length][input[1].length]);
}
let input = [];
rl.on('line', function(line) {
input.push(line);
}).on('close', () => {
solution(input);
process.exit();
});
DP를 활용한 LIS구현과, 이분 탐색을 활용한 LIS 구현 방법이 존재한다.ex) [5, 3, 1, 2, 6] 의 배열이 존재할 때,
위 배열로 만들 수 있는 증가 부분 수열 -> [5, 6], [3, 6], [1, 2, 6], [1, 6], [2, 6]
최장 증가 부분 수열 (LIS) -> [1, 2, 6]
O(N^2)를 가진다.즉 앞 순서의 모든 원소에서 끝나는 최장 증가 수열들의 길이 중, 가장 긴 것을 골라 1을 더한 것이 현재 수에서 끝나는 최장 증가 수열의 길이다.
따라서dp[i] = i번째 인덱스에서 끝나는 최장 증가 수열의 길이
dp[j] > count 해당 부분이다.const list = [5, 3, 1, 2, 6];
// 특정 원소에서 끝나는 LIS의 최소값 설정
let dp = new Array(list.length).fill(0);
for(let i = 0; i < n; i++) {
let count = 0;
for (let j = 0; j < i; j++) {
// i 번째 이전에 위치한 모든 원소에 대해, 해당 원소에서 끝나는 LIS의 길이를 확인한다.
if (list[i] > list[j] && dp[j] > count) {
count = dp[j];
}
}
// 이전 원소에서 끝나는 LIS에 +1 (현재 수)를 더한 새로운 LIS 길이
dp[i] = ++count;
}
console.log(Math.max(...dp));
O(logn)을 가진다.// value가 arr에 위치할 인덱스를 구하는 함수
function binarySearch (list, left, right, value) {
while(left < right) {
let mid = Math.floor((left + right) / 2);
if (list[mid] < value) {
left = mid + 1;
} else if (list[mid] > value) {
right = mid;
} else {
return mid;
}
}
return right;
}
function solution (N, list) {
const arr = [];
arr.push(list[0]);
for (let i = 1; i < N; i++) {
// arr의 마지막 원소보다 list[i]가 클 경우에는 push
if (arr[arr.length - 1] < list[i]){
arr.push(list[i]);
}
else {
// arr에 오름차순으로 위치할 인덱스 찾기
let index = binarySearch(arr, 0, arr.length - 1, list[i]);
arr[index] = list[i];
}
}
// LIS의 길이
console.log(arr.length);
}
안녕하세요 :) 다름이 아니라 이분탐색으로 LIS 배열을 구할 때, 최종적으로 완성된 LIS배열은 실제 LIS배열이 아니라 길이만 같은 더미 배열이라고 봐도 무방할까요?