LIS
LIS; Longest Increasing Subsequence
์ต์ฅ ์ฆ๊ฐ ๋ถ๋ถ ์์ด
original array์ ์ผ๋ถ ์์๋ฅผ ๊ณจ๋ผ๋ด์ ๋ง๋ ๋ถ๋ถ ์์ด ์ค, ๊ฐ ์์๊ฐ ์ด์ ์์๋ณด๋ค ํฌ๋ค๋ ์กฐ๊ฑด์ ๋ง์กฑํ๊ณ , ๊ทธ ๊ธธ์ด๊ฐ ์ต๋์ธ ๋ถ๋ถ ์์ด
LIS ๊ธธ์ด ๊ตฌํ๊ธฐ
1. DP - O(N^2)
length๋ฅผ ์๋์ ๊ฐ์ด ์ ์ํ ๋, DP๋ฅผ ํ์ฉํ์ฌ LIS์ ๊ธธ์ด๋ฅผ ๊ตฌํ ์ ์๋ค.
length[i]: i๋ฒ์งธ ์ธ๋ฑ์ค์์ ๋๋๋ ์ต์ฅ ์ฆ๊ฐ ๋ถ๋ถ ์์ด์ ๊ธธ์ด
์ด๋ length.size == array.size ์ด๋ค.
for (i in array.indices) {
length[i] = 1
for (j in 0 until i) {
if (array[j] < array[i]) length[i] = max(length[i], length[j] + 1)
}
}
๋ฐฐ์ด์์ idx๋ฅผ ํ๋์ฉ ๋๋ ค๊ฐ๋ฉด์ ๋ด๋ถ ๋ฐ๋ณต๋ฌธ์ผ๋ก 0~idx๊น์ง์ ์์๋ฅผ ํ์ธํ๋ค.
array[j] < array[i] ์ผ ๊ฒฝ์ฐ, length[i]๋ฅผ ์ ๋ฐ์ดํธํ๋ค.
- ๊ธฐ์กด์ length[i]
- j๋ฒ์งธ ์ธ๋ฑ์ค์์ ๋๋๋ ์ต์ฅ ์ฆ๊ฐ ๋ถ๋ถ ์์ด(length[j])๋ง์ง๋ง์ array[i]๋ฅผ ์ถ๊ฐํ์ ๋์ LIS ๊ธธ์ด
์์ ๋ฐฉ๋ฒ์ O(N^2)์ผ๋ก, ์๊ฐ์ด ์ค๋ ๊ฑธ๋ฆฐ๋ค. ์ด๋ถํ์ ์๊ณ ๋ฆฌ์ฆ์ ํ์ฉํ์ฌ ์๊ฐ์ ์ค์ด๋ ๋ฐฉ์์ด ์กด์ฌํ๋ค.
2. DP + BinarySearch - O(NLogN)
LIS๋ฅผ ๋ถ์์ ํ๊ฒ ์ ์ฅํ๋ฉด์ ์ด๋ถํ์ ์๊ณ ๋ฆฌ์ฆ์ ํ์ฉํ์ฌ ์๊ฐ๋ณต์ก๋๋ฅผ ๊ฐ์ ํ ๋ฐฉ์์ด ์กด์ฌํ๋ค.
var LIS = IntArray(10)
// LIS์ ์์ ์ธ๋ฑ์ค์ ๋ ์ธ๋ฑ์ค, num์ ๋ฐ์ LIS ๋ด๋ถ์์ num์ด ์์นํด์ผ ํ๋ idx๊ฐ์ ๋ฆฌํดํ๋ ํจ์
fun binarySearch(start: Int, end: Int, num: Int): Int {
var s = start
var e = end
while (s < e) {
var mid = (s + e) / 2
if (LIS[mid] < num) {
s = mid + 1
} else
e = mid
}
return e
}
// LIS์ idx๋ ๋ฐ๋ก ์ฌ์ฉํ๋ค
// LIS์ ์ฒซ๋ฒ์งธ ์์๋ array์ ์ฒซ๋ฒ์งธ ์์๋ก ์ด๊ธฐํ์ํจ๋ค.
var idx = 0
LIS[0] = array[0]
for (i in array.indices) {
if(LIS[idx] < array[i])
LIS[++idx] = array[i]
else
LIS[binarySearch(0, idx, array[i])] = array[i]
}
๋ฐฐ์ด์ ์ธ๋ฑ์ค๋ฅผ ์ฐจ๋ก๋ก ์ฆ๊ฐ์ํค๋ฉฐ, ํด๋น ์ธ๋ฑ์ค์ ์์๊ฐ LIS์ ๋ช ๋ฒ์งธ ์ธ๋ฑ์ค์ ๋ค์ด๊ฐ ์ ์๋์ง ์ด๋ถํ์์ผ๋ก ํ์ํ๋ค.
๋งค ๋ฐ๋ณต๋ฌธ๋ง๋ค LIS์ ๋ง์ง๋ง ์์์ ํ์ฌ array์ ์์๋ฅผ ๋น๊ตํ๋ค.
- ํ์ฌ array์ ์์๊ฐ ํด ๊ฒฝ์ฐ LIS์ ๋ง๋ถ์ธ๋ค.
- LIS์ ๋ง์ง๋ง ์์๊ฐ ํด ๊ฒฝ์ฐ ํ์ฌ array์ ์์๊ฐ ๊ธฐ์กด์ ์ ์ฅ๋ LIS์ ๋ด๋ถ์์ ์ด๋์ ์์นํ ์ ์๋์ง ์ด๋ถํ์ํ์ฌ ํด๋น ์ธ๋ฑ์ค์ ํ์ฌ array์ ์์๋ฅผ ์ ์ฅํ๋ค.
์ธ๋ถ๋ฐ๋ณต๋ฌธ O(N), ๋ด๋ถ๋ฐ๋ณต๋ฌธ O(logN)์ผ๋ก ์ด O(NlogN)์ผ๋ก ์์ DP ๋ฐฉ์๋ณด๋ค ํจ์ฌ ๊ฐ์ ๋ ์๊ฐ๋ณต์ก๋๋ฅผ ๋ณด์ธ๋ค.
โ ์ ๋ถ์์ ํ๊ฐ?
์์ ์ฝ๋์์ LIS ๋ LIS์ ๊ธธ์ด๋ฅผ ๊ตฌํ๊ธฐ ์ํ DP ๋ฐฐ์ด์ ์๋ฏธํ๋ ๊ฒ์ผ๋ก, ์ ํํ ์ต์ข LIS๋ฅผ ๋ด๊ณ ์์ง ์๋ค๋ ์ ์ ์ ์ํ์.
ํด๋น ์๊ณ ๋ฆฌ์ฆ์ผ๋ก ์ ํ์ฑ์ ๋ณด์ฅํ ์ ์๋ ์ ๋ณด๋ ๊ธธ์ด๋ฟ์ด๋ค.
๋์ ๋ฐฉ์ ๋ค์ฌ๋ค๋ณด๊ธฐ
์๋์ ํ๋ {1, 6, 4, 5, 2, 3, 4, 5}์์ LIS์ ๊ธธ์ด๋ฅผ ๊ตฌํ๊ธฐ ์ํด ์ ์ฝ๋์ ๋์์ ์์๋๋ก ๋ํ๋ธ ๊ฒ์ด๋ค.
idx/num | 1 | 6 | 4 | 5 | 2 | 3 | 4 | 5 |
0 | 1 | |||||||
1 | 1 | 6 | ||||||
2 | 1 | 4 | ||||||
3 | 1 | 4 | 5 | |||||
4 | 1 | 2 | 5 | |||||
5 | 1 | 2 | 3 | |||||
6 | 1 | 2 | 3 | 4 | ||||
7 | 1 | 2 | 3 | 4 | 5 |
- idx = 2: ๋ง์ง๋ง LIS์ ์์์ธ 6๋ณด๋ค ํ์ฌ array์ ์์์ธ 4๊ฐ ๋ ์์ผ๋ฏ๋ก ์ด๋ถํ์์ ์คํํ๋ค.
- ์ด๋ถํ์ ๊ฒฐ๊ณผ LIS[1] = 4
- idx = 3: ๋ง์ง๋ง LIS์ ์์์ธ 4๋ณด๋ค ํ์ฌ array์ ์์์ธ 5๊ฐ ๋ ํฌ๋ฏ๋ก LIS์ ๋ค์ ๋ง๋ถ์ธ๋ค.
- idx = 4: ํ์ฌ array์ ์์์ธ 2์ ์์น๋ฅผ ์ด๋ถํ์ํ๋ค.
- ์ด๋ถํ์ ๊ฒฐ๊ณผ LIS[1] = 2
โ LIS[1]์ด ๋ฎ์ด์์์ก์ด์. ์ด๋๋ ๋๋์?
idx = 4๊น์ง์ original array๋ {1, 6, 4, 5, 2}๋ก, ์ต์ข LIS๋ {1, 4, 5}์ด๋ค.
๊ทธ๋ฌ๋ ์ค์ ๋ก idx = 4์์์ LIS ๋ฅผ ์ถ๋ ฅํด ๋ณด๋ฉด {1, 2, 5}๊ฐ ์ ์ฅ๋์ด ์๋ค.
์ฆ, ์ด ์๊ณ ๋ฆฌ์ฆ์ ๊ฐ idx๋ง๋ค LIS์ ๊ธธ์ด๋ฅผ ์ ํํ๊ฒ ์ ์ฅํ์ง๋ง, ๋ค์์ ๋ค์ด์ฌ ์์๋ฅผ ์ํด ๋ถ์์ ํ๊ฒ LIS๋ฅผ ์ ์ฅํ๋ค.
์ค์ LIS ์ ์ฅํ๊ธฐ
์ค์ LIS๋ฅผ ์๊ณ ์ถ์ ๊ฒฝ์ฐ, array[i]๊ฐ LIS์ ๋ช ๋ฒ์งธ idx์ ์ ์ฅ๋๋์ง ๋ฐ๋ก ๊ธฐ๋กํด์ผ ํ๋ค.
์ ์ฝ๋๋ฅผ ์กฐ๊ธ ์์ ํ์ฌ record๋ผ๋ IntArray์ LIS ์ ๋ฐ์ดํธ ์ ๋ณด๋ฅผ ๊ธฐ๋กํด ๋ณด์.
fun binarySearch(start: Int, end: Int, num: Int): Int {
var s = start
var e = end
while (s < e) {
var mid = (s + e) / 2
if (LIS[mid] < num) {
s = mid + 1
} else
e = mid
}
return e
}
var idx = 0
LIS[0] = array[0]
// ์ฌ๊ธฐ๊ฐ ์ถ๊ฐ๋์์ด์
var record = IntArray(8)
for (i in array.indices) {
if (LIS[idx] < array[i]) {
LIS[++idx] = array[i]
// ์ฌ๊ธฐ๊ฐ ์ถ๊ฐ๋์์ด์
record[idx] = i
} else {
var bsIdx = binarySearch(0, idx, array[i])
LIS[bsIdx] = array[i]
// ์ฌ๊ธฐ๊ฐ ์ถ๊ฐ๋์์ด์
record[bsIdx] = i
}
}
record[i]์๋ LIS์ ์ ๋ฐ์ดํธ๋ array ์์์ idx๊ฐ์ ์ ์ฅํ๊ฒ ๋์๋ค.
์ฆ, LIS์์์ array[i]์ ์์น ์ ๋ณด๋ฅผ ์๊ฒ ๋ ๊ฒ์ด๋ค.
{1, 6, 4, 5, 2, 3, 4, 5}์์ ์ ์ฝ๋๋ฅผ ์คํ์์ผ record๋ฅผ ์ ์ฅํด ๋ณด๋ฉด ๋ค์๊ณผ ๊ฐ์ด ์ ์ฅ๋๋ค.
0 4 5 6 7 0 0 0
์ต์ข ์ ์ผ๋ก LIS๋ฅผ ์ถ๋ ฅํ๋ ค๋ฉด ๋ค์๊ณผ ๊ฐ์ด 0๋ถํฐ idx๊น์ง for๋ฌธ์ ์คํํด ์ฃผ๋ฉด ๋๋ค.
for (i in 0 .. idx) {
println(array[record[i]])
}
/*
์คํ ๊ฒฐ๊ณผ
1
2
3
4
5
*/
LIS
LIS; Longest Increasing Subsequence
์ต์ฅ ์ฆ๊ฐ ๋ถ๋ถ ์์ด
original array์ ์ผ๋ถ ์์๋ฅผ ๊ณจ๋ผ๋ด์ ๋ง๋ ๋ถ๋ถ ์์ด ์ค, ๊ฐ ์์๊ฐ ์ด์ ์์๋ณด๋ค ํฌ๋ค๋ ์กฐ๊ฑด์ ๋ง์กฑํ๊ณ , ๊ทธ ๊ธธ์ด๊ฐ ์ต๋์ธ ๋ถ๋ถ ์์ด
LIS ๊ธธ์ด ๊ตฌํ๊ธฐ
1. DP - O(N^2)
length๋ฅผ ์๋์ ๊ฐ์ด ์ ์ํ ๋, DP๋ฅผ ํ์ฉํ์ฌ LIS์ ๊ธธ์ด๋ฅผ ๊ตฌํ ์ ์๋ค.
length[i]: i๋ฒ์งธ ์ธ๋ฑ์ค์์ ๋๋๋ ์ต์ฅ ์ฆ๊ฐ ๋ถ๋ถ ์์ด์ ๊ธธ์ด
์ด๋ length.size == array.size ์ด๋ค.
for (i in array.indices) {
length[i] = 1
for (j in 0 until i) {
if (array[j] < array[i]) length[i] = max(length[i], length[j] + 1)
}
}
๋ฐฐ์ด์์ idx๋ฅผ ํ๋์ฉ ๋๋ ค๊ฐ๋ฉด์ ๋ด๋ถ ๋ฐ๋ณต๋ฌธ์ผ๋ก 0~idx๊น์ง์ ์์๋ฅผ ํ์ธํ๋ค.
array[j] < array[i] ์ผ ๊ฒฝ์ฐ, length[i]๋ฅผ ์ ๋ฐ์ดํธํ๋ค.
- ๊ธฐ์กด์ length[i]
- j๋ฒ์งธ ์ธ๋ฑ์ค์์ ๋๋๋ ์ต์ฅ ์ฆ๊ฐ ๋ถ๋ถ ์์ด(length[j])๋ง์ง๋ง์ array[i]๋ฅผ ์ถ๊ฐํ์ ๋์ LIS ๊ธธ์ด
์์ ๋ฐฉ๋ฒ์ O(N^2)์ผ๋ก, ์๊ฐ์ด ์ค๋ ๊ฑธ๋ฆฐ๋ค. ์ด๋ถํ์ ์๊ณ ๋ฆฌ์ฆ์ ํ์ฉํ์ฌ ์๊ฐ์ ์ค์ด๋ ๋ฐฉ์์ด ์กด์ฌํ๋ค.
2. DP + BinarySearch - O(NLogN)
LIS๋ฅผ ๋ถ์์ ํ๊ฒ ์ ์ฅํ๋ฉด์ ์ด๋ถํ์ ์๊ณ ๋ฆฌ์ฆ์ ํ์ฉํ์ฌ ์๊ฐ๋ณต์ก๋๋ฅผ ๊ฐ์ ํ ๋ฐฉ์์ด ์กด์ฌํ๋ค.
var LIS = IntArray(10)
// LIS์ ์์ ์ธ๋ฑ์ค์ ๋ ์ธ๋ฑ์ค, num์ ๋ฐ์ LIS ๋ด๋ถ์์ num์ด ์์นํด์ผ ํ๋ idx๊ฐ์ ๋ฆฌํดํ๋ ํจ์
fun binarySearch(start: Int, end: Int, num: Int): Int {
var s = start
var e = end
while (s < e) {
var mid = (s + e) / 2
if (LIS[mid] < num) {
s = mid + 1
} else
e = mid
}
return e
}
// LIS์ idx๋ ๋ฐ๋ก ์ฌ์ฉํ๋ค
// LIS์ ์ฒซ๋ฒ์งธ ์์๋ array์ ์ฒซ๋ฒ์งธ ์์๋ก ์ด๊ธฐํ์ํจ๋ค.
var idx = 0
LIS[0] = array[0]
for (i in array.indices) {
if(LIS[idx] < array[i])
LIS[++idx] = array[i]
else
LIS[binarySearch(0, idx, array[i])] = array[i]
}
๋ฐฐ์ด์ ์ธ๋ฑ์ค๋ฅผ ์ฐจ๋ก๋ก ์ฆ๊ฐ์ํค๋ฉฐ, ํด๋น ์ธ๋ฑ์ค์ ์์๊ฐ LIS์ ๋ช ๋ฒ์งธ ์ธ๋ฑ์ค์ ๋ค์ด๊ฐ ์ ์๋์ง ์ด๋ถํ์์ผ๋ก ํ์ํ๋ค.
๋งค ๋ฐ๋ณต๋ฌธ๋ง๋ค LIS์ ๋ง์ง๋ง ์์์ ํ์ฌ array์ ์์๋ฅผ ๋น๊ตํ๋ค.
- ํ์ฌ array์ ์์๊ฐ ํด ๊ฒฝ์ฐ LIS์ ๋ง๋ถ์ธ๋ค.
- LIS์ ๋ง์ง๋ง ์์๊ฐ ํด ๊ฒฝ์ฐ ํ์ฌ array์ ์์๊ฐ ๊ธฐ์กด์ ์ ์ฅ๋ LIS์ ๋ด๋ถ์์ ์ด๋์ ์์นํ ์ ์๋์ง ์ด๋ถํ์ํ์ฌ ํด๋น ์ธ๋ฑ์ค์ ํ์ฌ array์ ์์๋ฅผ ์ ์ฅํ๋ค.
์ธ๋ถ๋ฐ๋ณต๋ฌธ O(N), ๋ด๋ถ๋ฐ๋ณต๋ฌธ O(logN)์ผ๋ก ์ด O(NlogN)์ผ๋ก ์์ DP ๋ฐฉ์๋ณด๋ค ํจ์ฌ ๊ฐ์ ๋ ์๊ฐ๋ณต์ก๋๋ฅผ ๋ณด์ธ๋ค.
โ ์ ๋ถ์์ ํ๊ฐ?
์์ ์ฝ๋์์ LIS ๋ LIS์ ๊ธธ์ด๋ฅผ ๊ตฌํ๊ธฐ ์ํ DP ๋ฐฐ์ด์ ์๋ฏธํ๋ ๊ฒ์ผ๋ก, ์ ํํ ์ต์ข LIS๋ฅผ ๋ด๊ณ ์์ง ์๋ค๋ ์ ์ ์ ์ํ์.
ํด๋น ์๊ณ ๋ฆฌ์ฆ์ผ๋ก ์ ํ์ฑ์ ๋ณด์ฅํ ์ ์๋ ์ ๋ณด๋ ๊ธธ์ด๋ฟ์ด๋ค.
๋์ ๋ฐฉ์ ๋ค์ฌ๋ค๋ณด๊ธฐ
์๋์ ํ๋ {1, 6, 4, 5, 2, 3, 4, 5}์์ LIS์ ๊ธธ์ด๋ฅผ ๊ตฌํ๊ธฐ ์ํด ์ ์ฝ๋์ ๋์์ ์์๋๋ก ๋ํ๋ธ ๊ฒ์ด๋ค.
idx/num | 1 | 6 | 4 | 5 | 2 | 3 | 4 | 5 |
0 | 1 | |||||||
1 | 1 | 6 | ||||||
2 | 1 | 4 | ||||||
3 | 1 | 4 | 5 | |||||
4 | 1 | 2 | 5 | |||||
5 | 1 | 2 | 3 | |||||
6 | 1 | 2 | 3 | 4 | ||||
7 | 1 | 2 | 3 | 4 | 5 |
- idx = 2: ๋ง์ง๋ง LIS์ ์์์ธ 6๋ณด๋ค ํ์ฌ array์ ์์์ธ 4๊ฐ ๋ ์์ผ๋ฏ๋ก ์ด๋ถํ์์ ์คํํ๋ค.
- ์ด๋ถํ์ ๊ฒฐ๊ณผ LIS[1] = 4
- idx = 3: ๋ง์ง๋ง LIS์ ์์์ธ 4๋ณด๋ค ํ์ฌ array์ ์์์ธ 5๊ฐ ๋ ํฌ๋ฏ๋ก LIS์ ๋ค์ ๋ง๋ถ์ธ๋ค.
- idx = 4: ํ์ฌ array์ ์์์ธ 2์ ์์น๋ฅผ ์ด๋ถํ์ํ๋ค.
- ์ด๋ถํ์ ๊ฒฐ๊ณผ LIS[1] = 2
โ LIS[1]์ด ๋ฎ์ด์์์ก์ด์. ์ด๋๋ ๋๋์?
idx = 4๊น์ง์ original array๋ {1, 6, 4, 5, 2}๋ก, ์ต์ข LIS๋ {1, 4, 5}์ด๋ค.
๊ทธ๋ฌ๋ ์ค์ ๋ก idx = 4์์์ LIS ๋ฅผ ์ถ๋ ฅํด ๋ณด๋ฉด {1, 2, 5}๊ฐ ์ ์ฅ๋์ด ์๋ค.
์ฆ, ์ด ์๊ณ ๋ฆฌ์ฆ์ ๊ฐ idx๋ง๋ค LIS์ ๊ธธ์ด๋ฅผ ์ ํํ๊ฒ ์ ์ฅํ์ง๋ง, ๋ค์์ ๋ค์ด์ฌ ์์๋ฅผ ์ํด ๋ถ์์ ํ๊ฒ LIS๋ฅผ ์ ์ฅํ๋ค.
์ค์ LIS ์ ์ฅํ๊ธฐ
์ค์ LIS๋ฅผ ์๊ณ ์ถ์ ๊ฒฝ์ฐ, array[i]๊ฐ LIS์ ๋ช ๋ฒ์งธ idx์ ์ ์ฅ๋๋์ง ๋ฐ๋ก ๊ธฐ๋กํด์ผ ํ๋ค.
์ ์ฝ๋๋ฅผ ์กฐ๊ธ ์์ ํ์ฌ record๋ผ๋ IntArray์ LIS ์ ๋ฐ์ดํธ ์ ๋ณด๋ฅผ ๊ธฐ๋กํด ๋ณด์.
fun binarySearch(start: Int, end: Int, num: Int): Int {
var s = start
var e = end
while (s < e) {
var mid = (s + e) / 2
if (LIS[mid] < num) {
s = mid + 1
} else
e = mid
}
return e
}
var idx = 0
LIS[0] = array[0]
// ์ฌ๊ธฐ๊ฐ ์ถ๊ฐ๋์์ด์
var record = IntArray(8)
for (i in array.indices) {
if (LIS[idx] < array[i]) {
LIS[++idx] = array[i]
// ์ฌ๊ธฐ๊ฐ ์ถ๊ฐ๋์์ด์
record[idx] = i
} else {
var bsIdx = binarySearch(0, idx, array[i])
LIS[bsIdx] = array[i]
// ์ฌ๊ธฐ๊ฐ ์ถ๊ฐ๋์์ด์
record[bsIdx] = i
}
}
record[i]์๋ LIS์ ์ ๋ฐ์ดํธ๋ array ์์์ idx๊ฐ์ ์ ์ฅํ๊ฒ ๋์๋ค.
์ฆ, LIS์์์ array[i]์ ์์น ์ ๋ณด๋ฅผ ์๊ฒ ๋ ๊ฒ์ด๋ค.
{1, 6, 4, 5, 2, 3, 4, 5}์์ ์ ์ฝ๋๋ฅผ ์คํ์์ผ record๋ฅผ ์ ์ฅํด ๋ณด๋ฉด ๋ค์๊ณผ ๊ฐ์ด ์ ์ฅ๋๋ค.
0 4 5 6 7 0 0 0
์ต์ข ์ ์ผ๋ก LIS๋ฅผ ์ถ๋ ฅํ๋ ค๋ฉด ๋ค์๊ณผ ๊ฐ์ด 0๋ถํฐ idx๊น์ง for๋ฌธ์ ์คํํด ์ฃผ๋ฉด ๋๋ค.
for (i in 0 .. idx) {
println(array[record[i]])
}
/*
์คํ ๊ฒฐ๊ณผ
1
2
3
4
5
*/