Доказательство решения с линейным временем
Я напишу правое расширение , означающее увеличение правой конечной точки диапазона на 1, и левое сокращение означать увеличение левой конечной точки диапазона на 1. Этот ответ является небольшим отклонением ответа Аасмунда Элдхусета .Разница здесь в том, что как только мы найдем наименьшее j такое, что [0, j] содержит все интересные числа, мы будем рассматривать только диапазоны, которые содержат все интересные числа.(Можно интерпретировать ответ Aasmund таким образом, но также можно интерпретировать его как допускающее потерю одного интересного числа из-за сжатия влево - алгоритм, правильность которого еще предстоит установить.)
Основная идея заключается в том, что для каждой позиции j мы найдем самый короткий удовлетворяющий диапазон, заканчивающийся в позиции j, учитывая, что мы знаем самый короткий удовлетворяющий диапазон, заканчивающийся в позиции j-1.
РЕДАКТИРОВАТЬ: Исправлен сбой в базовом случае.
Базовый случай: найдите наименьшее j 'такое, что [0, j'] содержит все интересные числа.По построению не может быть диапазонов [0, k наименьший наибольший i такой, что [i, j '] содержит все интересные числа (т. Е. Удерживайте j' фиксированным).Это наименьший удовлетворяющий диапазон, заканчивающийся в позиции j '.
Чтобы найти наименьший удовлетворяющий диапазон, заканчивающийся в любой произвольной позиции j, мы можем расширить самый маленький удовлетворяющий диапазон, заканчивающийся в позиции j-1, на 1 позицию.Этот диапазон также обязательно будет содержать все интересные числа, хотя он может быть не минимальной длины. Тот факт, что мы уже знаем, что это удовлетворительный диапазон, означает, что нам не нужно беспокоиться о расширении диапазона «назад» влево, поскольку это может только увеличить диапазон на его минимальную длину (т.е. принять решениехуже.Таким образом, левая конечная точка диапазона должна быть продвинута как можно дальше, пока это свойство выполняется.Когда больше никаких сокращений влево не может быть выполнено, у нас есть диапазон удовлетворения минимальной длины, заканчивающийся на j (поскольку дальнейшие сокращения влево явно не могут сделать диапазон удовлетворяющим снова), и мы закончили.
Поскольку мы выполняем этодля каждой крайней правой позиции j мы можем взять диапазон минимальной длины по всем крайним правым позициям, чтобы найти общий минимум.Это можно сделать, используя вложенный цикл, в котором j продвигается в каждом цикле внешнего цикла.Ясно, что j продвигается в 1 n раз.Поскольку в любой момент времени нам когда-либо понадобится только крайняя левая позиция наилучшего диапазона для предыдущего значения j, мы можем сохранить его в i и просто обновить его по ходу работы.i начинается с 0, всегда <= j <= n, и только когда-либо продвигается вверх на 1, что означает, что он может продвигаться не более n раз.И i, и j продвигаются не более n раз, что означает, что алгоритм имеет линейное время. </p>
В следующем псевдокоде я объединил обе фазы в один цикл.Мы только пытаемся сжать левую сторону, если мы достигли стадии наличия всех интересных чисел:
# x[0..m-1] is the array of interesting numbers.
# Load them into a hash/dictionary:
For i from 0 to m-1:
isInteresting[x[i]] = 1
i = 0
nDistinctInteresting = 0
minRange = infinity
For j from 0 to n-1:
If count[a[j]] == 0 and isInteresting[a[j]]:
nDistinctInteresting++
count[a[j]]++
If nDistinctInteresting == m:
# We are in phase 2: contract the left side as far as possible
While count[a[i]] > 1 or not isInteresting[a[i]]:
count[a[i]]--
i++
If j - i < minRange:
(minI, minJ) = (i, j)
count[]
и isInteresting[]
- хэши / словари (или простые массивы, если задействованные числа маленькие).