Примітка про методологію
Я трохи подумав над цією проблемою і прийшов до вирішення. Коли я прочитав відповідь Саїда Амірі , я зрозумів, що те, що я придумав, - це спеціалізована версія стандартного алгоритму пошуку найдовшого підпорядкування для послідовності довжини 3. Я розміщую так, як я придумала рішення, тому що вважаю це є цікавим прикладом вирішення проблем.
Двоелементна версія
Почнемо з малого: замість того, щоб шукати три індекси, за якими елементи знаходяться в порядку, давайте шукатимемо два: такий, що .A [ i ] < A [ j ]i<jA[i]<A[j]
Якщо зменшується (тобто , або рівнозначно ), таких показників немає. В іншому випадку є індекс такий, що .A∀i<j,A[i]≥A[j]∀i,A[i]≥A[i+1]iA[i]<A[i+1]
Цей випадок дуже простий; ми спробуємо узагальнити це. Це показує, що заявлена проблема не вирішена: запитувані індекси не завжди існують. Тому ми швидше попросимо, щоб алгоритм або повертав дійсні індекси, якщо вони існують, або правильно стверджує, що таких індексів не існує.
Складання алгоритму
Я буду використовувати термін підпослідовність означає виписку з масиву , що складається з індексів , які не можуть бути послідовними ( з ), і працювати , щоб середнє послідовні елементи ( ).A(A[i1],…,A[im])i1<⋯<imA(A[i],A[i+1],…,A[i+m−1])
Ми щойно побачили, що запитувані індекси не завжди існують. Нашою стратегією має бути вивчення, коли індекси не існують. Ми зробимо це, припустивши, що ми намагаємося знайти індекси та бачимо, як наш пошук може піти не так. Тоді випадки, коли пошук не помиляється, нададуть алгоритм пошуку індексів.
За допомогою двох індексів ми могли б знайти послідовні індекси. Маючи три індекси, ми можемо не придумати і . Однак ми можемо розглянути той випадок, коли має бути вирішено пробіг трьох строго зростаючих елементів ( , оскільки легко розпізнати такі прогони, і подивіться, як ця умова не може бути виконана. Припустимо, що послідовність не має строго зростаючого пробігу довжиною 3.j=i+1k=j+1A[i]<A[i+1]<A[i+2]
Послідовність має лише строго зростаючі прогони довжиною 2 (які я називатиму впорядкованими парами для коротких), розділених зменшенням пробігу довжини щонайменше 2. Для того, щоб строго збільшувати пробіг щоб бути частиною зростаючої 3-елементної послідовності, повинен бути більш ранній елемент такий, що або пізніший елемент такий, що .A[j]<A[j+1]iA[i]<A[j]kA[j+1]<A[k]
Випадок, коли ні ні існує, коли кожна впорядкована пара повністю нижча за наступну. Це ще не все: коли пари переплітаються, нам потрібно порівняти їх більш тонко.ik
Крайній лівий елемент зростаючої послідовності повинен прийти рано і бути малим. Наступний елемент повинен бути більшим, але якомога меншим, щоб можна було знайти третій більший елемент . Перший елемент не завжди є найменшим елементом у послідовності, і він не завжди є першим, для якого є наступний більший елемент - іноді далі є нижчий 2-елементний подальший запас, а іноді і кращий підходить для вже знайденого мінімуму.ijki
Переходячи зліва направо, ми орієнтовно вибираємо найменший елемент як . Якщо ми знайдемо більший елемент далі праворуч, виберемо цю пару як орієнтовну . Якщо ми знайдемо ще більший , ми виграємо. Ключове, що слід зазначити, - це те, що наш вибір та вибір оновлюються незалежно: якщо у нас є кандидат і ми знаходимо таким, що , стає наступним кандидатом але залишається. Тільки якщо ми знайдемо таким, що будеi(i,j)ki(i,j)(i,j)i′>jA[i′]<A[i]i′i(i,j)j′A[j′]<A[j](i′,j′) стати новою кандидатською парою.
Заява алгоритму
Дано в синтаксисі Python, але будьте уважні, що я його не перевіряв.
def subsequence3(A):
"""Return the indices of a subsequence of length 3, or None if there is none."""
index1 = None; value1 = None
index2 = None; value2 = None
for i in range(0,len(A)):
if index1 == None or A[i] < value1:
index1 = i; value1 = A[i]
else if A[i] == value1: pass
else if index2 == None:
index2 = (index1, i); value2 = (value1, A[i])
else if A[i] < value2[1]:
index2[1] = i; value2[1] = A[i]
else if A[i] > value2[1]:
return (index2[0], index2[1], i)
return None
Ескіз доказу
index1
- індекс мінімальної частини масиву, який вже пройшов (якщо він трапляється кілька разів, ми зберігаємо перше виникнення) або None
перед обробкою першого елемента. index2
зберігає індекси зростаючої підрядності довжини 2 у вже пройденій частині масиву, що має найнижчий найбільший елемент, або None
якщо такої послідовності не існує.
Коли return (index2[0], index2[1], i)
працює, ми маємо value2[0] < value[1]
(це інваріант value2
) та value[1] < A[i]
(очевидно з контексту). Якщо цикл закінчується без виклику раннього повернення, або value1 == None
в цьому випадку не збільшується послідовність довжини 2, не кажучи вже про 3, або value1
містить збільшується послідовність довжини 2, яка має найменший найбільший елемент. В останньому випадку ми, крім того, маємо інваріант, що жодна збільшувана послідовність довжини 3 не закінчується раніше, ніж value1
; тому останній елемент будь-якої такої підпорядкованості, доданий до value2
, формував би зростаючу послідовність довжиною 3: оскільки ми також маємо інваріант, який value2
не є частиною зростаючої послідовності довжини 3, що міститься у вже пройденій частині масиву, немає такої підпорядкованості у всьому масиві.
Доведення вищезгаданих інваріантів залишається вправою для читача.
Складність
Ми використовуємо додаткову пам'ять і прокладаємо масив як потік зліва направо. Ми виконуємо обробку для кожного елемента, що призводить до часу виконання .O ( 1 ) O ( n )O(1)O(1)O(n)
Офіційне підтвердження
Залишене як вправу для читача.