字尾陣列sa(x)表示排序後第x位在排序前的位置。
這個東西的求法有兩種,一種是倍增,時間複雜度o(n log n)或o(n log2n),另一種是用不知道什麼方法做到的o(n)。
至於第二種方法是什麼,並不對勁的人並不知道,所以只說倍增。
考慮正常地比較兩個字串,都是從頭比較到尾:
那麼,如果把兩個字串都斷成兩半,並且已知每一段的排名,就相當於以第一段為第一關鍵字,第二段為第二關鍵字排序了。
根據這個性質,就能想到如果先把字串的每個位置開始長度為一的子串進行排序後,就能在至多n log n的時間內將每個位置開始長度為二的子串排序。
↑大概長這樣,注意最後要補乙個空字元。
以此類推,就能這樣倍增地求出字尾的排序了,還是要注意最後補空字元。
如果用基數排序,每次排序的時間複雜度是o(n)的,那麼總複雜度就是o(n log n)了。
如果用快速排序,總複雜度就是o(n log2n),心中有黨常數極小才能過。
#include#include#include#include#include#include#include#include#include#include#include#define rep(i,x,y) for(register int i=(x);i<=(y);++i)#define dwn(i,x,y) for(register int i=(x);i>=(y);--i)
#define re register
#define maxn 2000010
using namespace std;
inline int read()
void write(int x)
while(x)ch[++ff]=(x%10)+'0',x/=10;
if(ff==0)putchar('0');
while(ff)putchar(ch[ff--]);
putchar(' ');
}
int sa[maxn],ord[maxn],rnk[maxn],n,m;
int sum[maxn];
char s[maxn];
int main()
rep(i,1,n)write(sa[i]);
return 0;
}
並不對勁的splay
splay和不加任何旋轉一定會被卡的二叉搜尋樹的唯一區別就是每次操作把當前節點旋轉到根。旋轉有各種zig zag的組合方式,感覺很麻煩,並不對勁的人並不想講。其實可以找出一些共性將它們合併。設ls a 點a是其父親的左兒子 son a 0 a的左兒子,son a 1 a的右兒子,fa a a的父親。...
並不對勁的費用流
最小費用最大流肯定要保證最大流,所以它和最大流有一些類似的性質。如果把費用看成邊,就可以每次走最短路 保證費用最小 走到不能走為止 保證最大流 費用流版的ek就是這樣。需要注意的是,反向弧的邊權為它對應的正向弧的費用的相反數,所以最短路要用spfa來求。費用流版的dinic,又叫zkw費用流,還是多...
並不對勁的卡常技巧
俗話說的好,心中有黨,常數極小。1 迴圈中加暫存器優化for register int i i n i 好像在開o2時這個沒什麼用。2 函式前inline int f int x 當呼叫函式本身的時間長度比執行函式的更長時效果會更顯著。配合read write 使用更佳。3 將取模運算改成if x ...