P1177 【模板】快速排序
阿新 • • 發佈:2021-07-20
本文為倍增做法
字尾陣列題
字尾陣列是啥,把所有的字尾排個序就是字尾陣列了
顯然的暴力做法就是全部sort一遍
這不白瞎
我們利用倍增的思想,顯然可以把一個字串分成兩半進行比較就可以了
引用一下wiki的圖片。
這裡有兩個陣列
\(SA_i\)表示第i小的字尾的編號
而\(RK_i\)表示第i個字尾的排完序後的編號
然後倍增,就是\(O(n log^2n)\)的做法了。
但是可以再搞掉一個log,就是把sort換成基數排序
#include <iostream> #include <cstdio> #include <algorithm> #include <cstring> using namespace std; const int maxn = 1000010; char s[maxn]; int n, sa[maxn], rk[maxn], oldrk[maxn << 1], id[maxn], cnt[maxn]; int m, p, w; int main() { scanf("%s", s + 1); n = strlen(s + 1); m = max(n, 300); for (int i = 1; i <= n; ++i) ++cnt[rk[i] = s[i]]; for (int i = 1; i <= m; ++i) cnt[i] += cnt[i - 1]; for (int i = n; i >= 1; --i) sa[cnt[rk[i]]--] = i; for (int w = 1; w < n; w <<= 1) { memset(cnt, 0, sizeof(cnt)); for (int i = 1; i <= n; ++i) id[i] = sa[i]; for (int i = 1; i <= n; ++i) ++cnt[rk[id[i] + w]]; for (int i = 1; i <= m; ++i) cnt[i] += cnt[i - 1]; for (int i = n; i >= 1; --i) sa[cnt[rk[id[i] + w]]--] = id[i]; memset(cnt, 0, sizeof(cnt)); for (int i = 1; i <= n; ++i) id[i] = sa[i]; for (int i = 1; i <= n; ++i) ++cnt[rk[id[i]]]; for (int i = 1; i <= m; ++i) cnt[i] += cnt[i - 1]; for (int i = n; i >= 1; --i) sa[cnt[rk[id[i]]]--] = id[i]; memcpy(oldrk, rk, sizeof(rk)); int i; for (i = 1, p = 0; i <= n; ++i) { if (oldrk[sa[i]] == oldrk[sa[i - 1]] && oldrk[sa[i] + w] == oldrk[sa[i - 1] + w]) { rk[sa[i]] = p; } else { //記得去重哦 rk[sa[i]] = ++p; } } } for (int i = 1; i <= n; ++i) printf("%d ", sa[i]); return 0; }
這樣的話還是有點慢,可以進行優化,每一次迴圈我們算出來的p,事實上就是下一次迴圈的值域。而且每一次的第二關鍵字,就是上一次的第一關鍵字,所以不用再排一遍\(rk_{id_i}\)完全可以存下來,減少不連續記憶體訪問,同理,我們可以吧if換成個cmp
#include<iostream> #include<cstdio> #include<algorithm> #include<cstring> using namespace std; const int maxn=1000010; char s[maxn]; int n,sa[maxn],rk[maxn],oldrk[maxn<<1],id[maxn],cnt[maxn]; int m,p,w; int tem[maxn]; int i; bool cmp(int x, int y, int w) { return oldrk[x] == oldrk[y] && oldrk[x + w] == oldrk[y + w]; } int main(){ scanf("%s",s+1); n=strlen(s+1); m=max(n,300); for(int i=1;i<=n;++i) ++cnt[rk[i]=s[i]]; for(int i=1;i<=m;++i) cnt[i]+=cnt[i-1]; for(int i=n;i>=1;--i) sa[cnt[rk[i]]--]=i; for(int w=1;w<n;w<<=1,m=p){ for (p = 0,i = n; i > n - w; --i) id[++p] = i; for (i = 1; i <= n; ++i) { if (sa[i] > w) id[++p] = sa[i] - w; } memset(cnt,0,sizeof(cnt)); for(int i=1;i<=n;++i) ++cnt[tem[i]=rk[id[i]]]; for(int i=1;i<=m;++i) cnt[i]+=cnt[i-1]; for(int i=n;i>=1;--i) sa[cnt[tem[i]]--]=id[i]; memcpy(oldrk,rk,sizeof(rk)); int i; for( i=1,p=0;i<=n;++i){ rk[sa[i]] = cmp(sa[i], sa[i - 1], w) ? p : ++p; } if(p==n){ for(int i=1;i<=n;++i){ sa[rk[i]]=i; } break; } } for(int i=1;i<=n;++i) printf("%d ",sa[i]); return 0; }