最長不下降子串行詳解(一)

2021-08-26 05:39:25 字數 2776 閱讀 1140

由2023年位元組跳動第二次筆試開始學習,第一部分參考:最長不下降子串行nlogn以及輸出序列,作者milky-way

對於普通的最長不下降子串行,每個數都要從頭開始遍歷,複雜度

**為:

利用序列的單調性

對於任意乙個單調序列,如 1 2 3 4 51 2 3 4 5 (是單增的),若這時向序列尾部增添乙個數 xx ,我們只會在意 xx 和 55 的大小,若 x>5x>5 ,增添成功,反之則失敗。由於普通**是從頭開始比較,而 xx 和 1,2,3,41,2,3,4 的大小比較是沒有用處的,這種操作只會造成時間的浪費,所以效率極低。對於單調序列,只需要記錄每個序列的最後乙個數,每增添乙個數 xx ,直接比較 xx 和末尾數的大小。只有最後乙個數才是有用的,它表示該序列的最大限度值。

實現方法就是新開乙個陣列 dd ,用它來記錄每個序列的末尾元素,以求最長不下降為例,d[k]d[k] 表示長度為k的不下降子串行的最小末尾元素。

我們用 lenlen 表示當前湊出的最長序列長度,也就是當前 dd 中的最後那個位置。

這樣就很 easyeasy 了,每讀入乙個數 xx ,如果 xx 大於等於 d[len]d[len] ,直接讓 d[len+1]=xd[len+1]=x ,然後 len++len++ ,相當於把 xx 接到了最長的序列後面;

如果 xx 小於 d[len]d[len] ,說明 xx 不能接到最長的序列後面,那就找 d[1...len−1]d[1...len−1] 中末尾數小於等於 xx 的的序列,然後把 xx 接到它後面。舉個例子,若當前 x==7,len==8x==7,len==8 :

d[1]⋯d[5]d[1]⋯d[5] 均小於等於 xx ,若在 d[1]d[1] 後接 xx ,則 d[2]d[2] 應換成 xx ,但 d[2]==3d[2]==3 ,比 xx 小,能接更多的數,用 77 去換 33 顯然是不划算的,所以 xx 不能接 d[1]d[1] 後。同理,d[2]⋯d[4]d[2]⋯d[4] 均不能接 xx 。由於 d[5]≤xd[5]≤x 且 x根據這個操作過程,易知陣列 dd 一定是單調的序列,所以查詢的時候可以用二分!二分效率是 lognlogn 的,所以整個演算法的效率就是 nlognnlogn 的啦~

對於最長不下降,可以用 stlstl 中的 upperbound()upperbound() 函式,比如上述操作可以寫為:

for (int i=2;i<=n;i++)

}但是,對於其他的單調序列,比如最長不上公升等等,需要縝密地考慮,根據情況來手寫二分。

注意 upperboundupperbound 是找單增序列中第乙個大於 xx 的,lowerboundlowerbound 是找單增序列中第乙個大於等於 xx 的,只要不是這兩個,都需要手寫二分。

**:

//最長不下降子串行nlogn  song 

#include#includeusing namespace std;

int a[40005];

int d[40005];

int main()

d[1]=a[1]; //初始化

int len=1;

for (int i=2;i<=n;i++)

}printf("%d\n",len);

return 0;

}最長不下降子串行 - nlogn

這時候需要增加乙個 cc 陣列 用來記錄每個元素在最長序列中的位置,即 c[i] 表示 a[i] 被放到了序列的第幾個位置。

輸出時,從 陣列 a 的尾部開始,逆序依次找出 c 為 len, len-1, len-2 … 3, 2, 1 的元素,並且找到乙個就接著尋找 c[i]-1,直到找到 c[i] 為 1 的數。

舉個例子:

a:1379

1638

2437

1844

1921

2263

15c:11

2344

5465

6783

len = 8;

我們從 15 開始倒著找 c 為 8 的元素,找到 63,接著找 c 為 7 的,找到 22,再找 c 為 6 的,找到 21,再找 c 為 5 …… 以此類推。

從而,我們得出的序列為 63,22,21,19,18,16,9,7

逆序輸出來,就是 7,9,16,18,19,21,22,63

為什麼這個方法是對的呢?倒序查詢保證了兩個條件:

- 如果 c 中有多個相同的數,後面的一定是最新更新的;

- 在保證一條件的前提下,倒序找,後面的數一定可以接到前面數的後面。」

**:

//from - milky way

#include #include #include using namespace std;

int d[100], c[100], a[100], len = 1;

int main()

d[1] = a[1], c[1] = 1;

for (int i = 2; i <= n; ++ i) else

}stacksta;

for (int i = n, j = len; i >= 1; -- i)

if (j == 0) break;

}printf("%d\n", len);

while (!sta.empty())

return 0;

}最長不下降之輸出子串行 - nlogn

最長不下降子串行詳解

解決的問題 給定乙個序列,求最長不下降子串行的長度 nlogn的演算法沒法求出具體的序列是什麼 定義 a 1.n 為原始序列,d k 表示長度為k的不下降子串行末尾元素的最小值,len表示當前已知的最長子序列的長度。初始化 d 1 a 1 len 1 0個元素的時候特判一下 現在我們已知最長的不下降...

最長不下降子串行

a1 t0 an a an 1 2 b an c d n 1 求該序列最長不下降子串行長度 n不是很大顯然可以暴力。n很大呢?那就不斷減迴圈節長度直至減到乙個閾值內,再暴力。正確性顯然,只要閾值不要設太小。include include include define fo i,a,b for i a...

最長不下降子串行

最長不下降子串行解法 第一種就是普通的dp方法 for int i 1 i n i dp 0 1 for int i 1 i n i ans max ans,dp i cout 主要記錄一下n logn的寫法 二分 主要思路 用乙個陣列 b 來記錄最長的子串行 一開始讓b 1 a 1 陣列a為輸入的...