1. LCS๋?
Longest Common Subsequence์ ์ฝ์์ด๋ฉฐ, ๋ ๋ฌธ์์ด์ ๋น๊ตํ ๋ ๊ณตํต์ ์ผ๋ก ๋ํ๋๋ ๋ถ๋ถ ์์๋ค ์ค ๊ฐ์ฅ ๊ธด ๊ฒ์ ๋งํ๋ค.
์๋ฅผ ๋ค์ด, 'ABCBDAB'์ 'BDCABA'์ LCS๋ฅผ ๊ตฌํด๋ณด๋ฉด
ABCBDAB
BDCABA
=> 'BCBA'๊ฐ ๋์ค๊ฒ ๋๋ค.
๊ทธ๋ฆฌ๊ณ LCS๋ ์ต์ ํ๋ฅผ ํด์ผ ํ๋ ๋ฌธ์ ์ด๊ธฐ ๋๋ฌธ์, ๋์ ๊ณํ๋ฒ(DP)๋ฅผ ์ฌ์ฉํ์ฌ์ผ ๊ฐ์ฅ ํจ์จ์ ์ผ๋ก ๊ตฌํ ์ ์๋ค.
1-1. ์ ๊ทผ ๋ฐฉ๋ฒ & ์์
๋ ๋ฌธ์์ด 'GCABCD' ๊ทธ๋ฆฌ๊ณ 'ABDCED'๊ฐ ์ฃผ์ด์ก์ ๋, LCS๋ฅผ ๊ตฌํ๋ ์งํ๊ณผ์ ์ ๋ค์๊ณผ ๊ฐ๋ค.
์ ๊ทธ๋ฆผ์์ ํ์ธํ ์ ์๋ฏ์ด, {GCA}, {GCAB}, {GCABC}, {GCABCD}์ {A}๋ฅผ ๋น๊ตํ๋ฉด ๊ณตํต ์์ด์ด 1์์ ์ ์ ์๋ค.
์ ๊ทธ๋ฆผ์ {ABDCED} ๋ฌธ์์ด์ 'B'๋ฅผ ์ถ๊ฐํ์ฌ ๋น๊ตํ ๊ฒ์ด๋ค. ๋ง์ฐฌ๊ฐ์ง๋ก, ์์ ๋ฌธ์์ด๊ณผ {AB}๋ฅผ ๋น๊ตํ๊ธฐ ๋๋ฌธ์ {GCAB} ์ดํ ๊ธธ์ด๊ฐ 2๊ฐ ๋จ์ ์ ์ ์๋ค.
์ด์ด์ ์งํ๋๋ ๊ณผ์ ๋ค์ ์ญ ๋ณด๋ฉด ๋ค์๊ณผ ๊ฐ๋ค.
์ด ๊ทธ๋ฆผ์ ํ ๋๋ก ์ ํ์์ ๊ตฌํด๋ณด๋ฉด ๋ค์๊ณผ ๊ฐ์ ๊ท์น๋ค์ด ์์์ ์ ์ ์๋ค.
LCS ๊ธธ์ด ์งํ ๊ท์น(์ ํ์)
1. ๋ฌธ์์ด์ ๋น๊ต๊ฐ ๊ฐ์ ๊ฒฝ์ฐ : ๋ถ๋ถ ๋ฌธ์์ด ์ฆ๊ฐ. ์ฆ, ๋๊ฐ์ ์ผ์ชฝ ์๊น์ง์ ์งํ์ ๊ธธ์ด + 1
2. ๋ฌธ์์ด์ ๋น๊ต๊ฐ ๋ค๋ฅธ ๊ฒฝ์ฐ : ํ์ฌ ์งํ์ํฉ์ ์ผ์ชฝ๊ณผ ์์ชฝ ๊ฐ์ค ๋ ํฐ ๊ฐ
์ฝ๋๋ก ๋ํ๋ด๋ฉด ์๋์ ๊ฐ๋ค.
// ๋ฌธ์์ดs1, s2
int n = s1.length();
int m = s2.length();
int[][] dp = new int[n+1][m+1];
if(s1.charAt(i-1) == s2.charAt(j-1)) dp[i][j] = dp[i-1][j-1] + 1;
if(s1.charAt(i-1) != s2.charAt(j-1)) dp[i][j] = Math.max(dp[i][j-1], dp[i-1][j]);
2. LIS๋?
Longest Increasing Subsequence์ ์ฝ์์ด๋ฉฐ, ๋ถ์ฐ์ ์๊ด์์ด ๊ฐ์ฅ ๊ธด ์ฆ๊ฐํ๋ ๋ถ๋ถ ์์ด์ ๊ตฌํ๋ ์๊ณ ๋ฆฌ์ฆ์ด๋ค.
์๋ค ๋ค์ด, ์์ด A = {10,20,10,30,20,50}์ด ์๋ค๊ณ ํ๋ฉด, ํด๋น ์์ด์ LIS๋ {10,20,30,50}์ด๋ค. ๊ธธ์ด๋ 4
ํ์ด ๋ฐฉ๋ฒ์ ์๋์ ๊ฐ์ด ๋๊ฐ์ง ๋ฐฉ์์ด ์๋ค.
1. DP(์๊ฐ๋ณต์ก๋ O(N^2)) -> ๊ฐ๋จํ์ง๋ง, N์ด ํด์๋ก ์๊ฐ์ด ์ค๋๊ฑธ๋ฆผ
2. ์ด๋ถํ์(์๊ฐ๋ณต์ก๋ O(nlogn)) -> ๋ณต์กํ์ง๋ง, N์ด ํด๋ ์ ๋ฆฌ
์ด์ ๋ ๊ฐ์ง ํ์ด๋ฐฉ๋ฒ์ ๋ํด ์์๋ณด์.
2-1. DP๋ฅผ ์ด์ฉํ ํ์ด
DP๋ฅผ ์ด์ฉํ ํ์ด๋ ๊ฐ๋จํ๋ค.
๋จผ์ , 1์ฐจ์ ๋ฐฐ์ด dp๋ฅผ ์ ์ธํด์ค๋ค.(์ ์ธ ํ 1๋ก ์ด๊ธฐํ)
(์ฌ๊ธฐ์, dp[i] = 0~i์ ์ฆ๊ฐํ๋ ๋ถ๋ถ ์์ด ํฌ๊ธฐ)
๋ค์, ์ด์ค ํฌ๋ฌธ์ ์ค๊ณํ์ฌ ๊ฐ ์๋ฅผ ๋น๊ตํด๊ฐ๋ฉด์ ์ฆ๊ฐํ๋ ๋ถ๋ถ ์์ด์ ํฌ๊ธฐ๋ฅผ ์นด์ดํธํด์ค๋ค.
1. i : 0 ~ n-1
2. j : i+1 ~ n-1
๋ง์ง๋ง์ผ๋ก, dp๋ฐฐ์ด์ค ๊ฐ์ฅ ํฐ ๊ฐ์ ์ถ๋ ฅํ๋ฉด ๋๋ค.
// numArr๋ ์
๋ ฅ๋ ์์ด์ ์์ฐจ์ ์ผ๋ก ๋ด์ ๋ฐฐ์ด
for(int i = 0; i < N; i++) {
for(int j = i+1; j < N; j++) {
if(numArr[i] < numArr[j]) {
dp[j] = Math.max(dp[j], dp[i] + 1);
}
}
}
int max = 0;
for(int d : dp) {
max = Math.max(max, d);
}
ํ์ง๋ง, ์ด๋ฐ ์ด์คfor๋ฌธ์ N๊ฐ์ด 10000์ด์์ด๋๋ฉด, ์๊ฐ์ด๊ณผ๊ฐ ๋ฐ์ํ๋ค.(O(N^2))
ํจ์จ์ฑ์ ์ฌ๋ฆฌ๊ธฐ ์ํด ์ด๋ถํ์์ ํตํ ์ต์ ํ ์์ ์ ๊ฑฐ์ณ ์๊ฐ๋ณต์ก๋๋ฅผ O(NlogN)๋ก ์ค์ฌ์ฃผ๋ ๋ฐฉ์์ ๋ํด ์์๋ณด์.
2-2. ์ด๋ถํ์์ ์ด์ฉํ ํ์ด(Lower Bound)
- LIS์ ๊ธธ์ด๋ง ๊ตฌํ ๋ ์ฌ์ฉ(์์ด์ ๊ตฌํ๊ธฐ ์ํด์ ๋ฐ๋ก ์ญ์ถ์ ๊ณผ์ ํ์)
์์ด์ ์ํํ๋ฉด์, ํ์ฌ ๊ฐ์ ์ ๊ท ๋ฐฐ์ด์ ๋ฃ๊ฑฐ๋ ์นํํ๋ ๋ฐฉ์์ผ๋ก ๊ตฌํ๋๊ณ ,
ํ์ฌ ๊ฐ์ Lower Bound๊ฐ ์๋ค๋ฉด ์ฝ์ ํ๊ณ , ์๋ค๋ฉด ํด๋น ๊ฐ์ ์ฐพ์์ ์นํํ๋ค.
์๋ฅผ ๋ค์ด, {10,20,10,30,20,50}์ด๋ผ๋ ์์ด์ด ์๋ค๊ณ ํ์.
1. LIS๋ฐฐ์ด์ ์์ด์ ์ฒซ ๋ฒ์จฐ ์์๋ฅผ ๋ฃ๋๋ค(10)
10 |
2. LIS๋ฐฐ์ด์ ๋ง์ง๋ง ์์(10)๊ฐ ํ์ฌ ์์(20)๋ณด๋ค ์๋ค๋ฉด Lower Bound๋ฅผ ์ฐพ์ ์ ์์ผ๋ฏ๋ก ๊ทธ๋๋ก ๊ฐ์ ๋ฃ์ด์ค๋ค.
10 | 20 |
3. ํ์ฌ ์์(10)์ด LIS๋ฐฐ์ด์ ๋ง์ง๋ง ์์(20)๋ณด๋ค ์๊ธฐ ๋๋ฌธ์, ์ด๋ถ ํ์์ ํตํด ์ ํฉํ ์๋ฆฌ๋ฅผ ์ฐพ๊ณ ํด๋น ๊ฐ์ผ๋ก ์นํํ๋ค.
10 | 20 |
4. ํ์ฌ ์์(30)์ด LIS๋ฐฐ์ด์ ๋ง์ง๋ง ์์(20)๋ณด๋ค ํฌ๊ธฐ ๋๋ฌธ์, ๊ทธ๋๋ก ๊ฐ์ ๋ฃ์ด์ค๋ค.
10 | 20 | 30 |
5. ํ์ฌ ์์(20)์ด LIS๋ฐฐ์ด์ ๋ง์ง๋ง ์์(30)๋ณด๋ค ์๊ธฐ ๋๋ฌธ์, ์ด๋ถ ํ์์ ํตํด ์ ํฉํ ์๋ฆฌ๋ฅผ ์ฐพ๊ณ ํด๋น ๊ฐ์ผ๋ก ์นํํ๋ค.
10 | 20 | 30 |
6. ํ์ฌ ์์(50)์ด LIS๋ฐฐ์ด์ ๋ง์ง๋ง ์์(30)๋ณด๋ค ํฌ๊ธฐ ๋๋ฌธ์, ๊ทธ๋๋ก ๊ฐ์ ๋ฃ์ด์ค๋ค.
10 | 20 | 30 | 50 |
์ฝ๋๋ ์๋์๊ฐ๋ค.
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.StringTokenizer;
public class Main {
public static void main(String[] args) throws Exception {
int[] lis = new int[N]; // LIS ๋ฐฐ์ด
int[] arr = new int[N]; // ์์ด ๋ฐฐ์ด
//์์ด ์ ์ฅ
StringTokenizer st = new StringTokenizer(br.readLine());
for(int i = 0; i < N; i++){
arr[i] = Integer.parseInt(st.nextToken());
}
lis[0] = arr[0]; // ์ฒซ๋ฒ์งธ ์์ ์ฝ์
int lisIdx = 0; // lis๋ฐฐ์ด์ ๋ง์ง๋ง ์์์ ์ธ๋ฑ์ค ๊ฐ
// ์์ด์ ์ํํ๋ฉด์ Lower Bound ๊ฐ์ ์ฐพ๋๋ค.
for(int i = 1; i < N; i++) {
// lis์ ๋ง์ง๋ง ์์๊ฐ ํ์ฌ๊ฐ ๋ณด๋ค ์์๋(Lower Bound ์กด์ฌX => ๊ทธ๋๋ก ์ฝ์
)
if(lis[lisIdx] < arr[i]) {
lisIdx++;
lis[lisIdx] = arr[i];
} else {
// ๋ง์ง๋ง ์์๊ฐ ํ์ฌ๊ฐ ๋ณด๋ค ํด ๋(lis๋ฐฐ์ด์์ arr[i]์ Lower Bound๊ฐ์ ํด๋นํ๋ index๋ฅผ ์ฐพ์์ผํจ)
int idx = lowerBound(0, lisIdx, arr[i]);
lis[idx] = arr[i];
}
}
System.out.println(lisIdx+1); // ๋ง์ง๋ง ์ธ๋ฑ์ค + 1 ํด์ผ ์ด ๊ธธ์ด๊ฐ๋จ
}
public static int lowerBound(int Left, int Right, int n) {
int mid = 0;
while(Left < Right) {
mid = (Left + Right) / 2;
// arr[i]์ ๊ฐ์ด lis๋ฐฐ์ด์ ์ค๊ฐ ๊ฐ๋ณด๋ค ์๊ฑฐ๋ ๊ฐ์ผ๋ฉด, ์ค๋ฅธ์ชฝ ์ธ๋ฑ์ค(Right) ํฌ๊ธฐ๋ฅผ ์ ๋ฐ์ผ๋ก ์ค์ธ๋ค.
if(n <= lis[mid]) {
Right = mid;
} else {
Left = mid + 1;
}
}
๐์ฐธ๊ณ ์ฌ์ดํธ
์ต์ฅ ๊ณตํต ๋ถ๋ถ ์์ด(LCS : Longest Common Subsequence)๋?
Longest Common Sub-sequence Algorithm(LCS, ์ต์ฅ ๊ณตํต ๋ถ๋ถ์์ด)
[Algorithm] LIS ์๊ณ ๋ฆฌ์ฆ (์ต์ฅ์ฆ๊ฐ์์ด ์๊ณ ๋ฆฌ์ฆ)