Решение задачи 300. Longest Increasing Subsequence
Дан массив целых чисел nums[]
длины n
. Требуется найти самую длинную строго восходящую подпоследовательнось его элементов. То есть, каждый следующий элемент подпоследовательности должен быть строго больше предыдущего. При этом элементы не обязательно следуют непосредственно друг за другом и являются соседними, однако относительное их расположение в подпоследовательности и исходном массиве одинаково. То есть, меньший элемент предшествует большему и в подпоследовательности и в исходном массиве.
Для решения задачи подходят принципы динамического программирования. Что, если бы на момент рассмотрения очередного элемента массива i
нам было бы известно на каких элементах nums[j...]
заканчиваются уже найденные восходящие подпоследовательности подмассива [0, i - 1]
?
Заведем массив dp_[]
длины n
, где для каждой позиции j
от 0
до n - 1
dp_[j]
содержит значение последнего элемента восходящей подпоследовательности длины j
. Согласно определению строго восходящей подпоследовательности элемент, предшествующий последнему будет строго меньше последнего. То есть dp_[j - 1] < dp_[j]
. Одновременно с этим элемент dp_[j - 1]
является последним элементом подпоследовательности длины j - 1
. Таким образом, для любого j
от 0
до n - 1
dp_[j - 1]
будет меньше dp_[j]
, а массив dp_[]
будет поддерживаться отсортированным по возрастанию. Это свойство позволяет пользоваться бинарным поиском, и в массиве dp_[]
с помощью метода std::upper_bound()
можно найти место для любого элемента массива nums[]
. Так как по условию мы ищем строго восходящую подпоследовательность, то перед размещением в массиве dp_
элемента массива nums[]
надо убедиться, что он не равен предшествующему элементу. Равенство с последующим проверять не нужно - алгоритм std::upper_bound
гарантирует возврат итератора элемента строго большего искомому, или end()
, если такой элемент отсутствует.
Теперь об алгоритме по порядку:
- В массиве
dp_[]
дляj
от0
доn - 1
dp_[j]
будет равен значению элемента из массиваnums[]
, на котором заканчивается восходящая подпоследовательность длиныj
. - Инициализируем массив
dp_[]
длиныn
значениемINT_MAX
. Для корректной работы алгоритма элементы массиваnums[]
должны быть меньше этого значения. - Инициализируем начальный элемент
dp_[0] = INT_MIN
- базовый случай. В непустом массивеnums[]
не может существовать восходящей подпоследовательности нулевой длины. - Заведем переменную
length
для сохранения длины максимальной восходящей подпоследовательности. - Для
i
от0
доn - 1
для каждого элементаnums[i]
бинарным поискомstd::upper_bound()
по массивуdp_[]
ищем позициюj
перед которой мы могли бы разместить элементnums[i]
, при этом проверяем, чтоdp_[j - 1] < nums[i]
. Если условие выполняется, присваиваемdp_[j] = nums[i]
. Обновляем переменнуюlength = std::max(length, j)
. - Возвращаем ответ -
length
.
Моя реализация здесь.