用途
考慮”ababcad”
那麼就可以分成7個這樣的字串:
1 2 3 4 5 6 7
| 0 - "ababcad" 1 - "babcad" 2 - "abcad" 3 - "bcad" 4 - "cad" 5 - "ad" 6 - "d"
|
然後再把這7個字串按照ASCII碼的順序排序,變成這樣:
1 2 3 4 5 6 7
| 0 - "ababcad" 2 - "abcad" 5 - "ad" 1 - "babcad" 3 - "bcad" 4 - "cad" 6 - "d"
|
所以順序就是0251346
算法
考慮倍增
先把字串在後面補個極小字元(假設$),更後面的就亂擺(方便起見就讓它變成環吧),變成
1 2 3 4 5 6 7 8
| 0 - "ababcad$" 1 - "babcad$a" 2 - "abcad$ab" 3 - "bcad$aba" 4 - "cad$abab" 5 - "ad$ababc" 6 - "d$ababca" 7 - "$ababcad"
|
可以發現大小不會變,因為有一個極小字元。
然後先取第一個字元
1 2 3 4 5 6 7 8
| 0 - a 1 - b 2 - a 3 - b 4 - c 5 - a 6 - d 7 - $
|
直接用std::sort $\mathcal{O}(NlogN)$後再編號進rk陣列(可以想成離散化)。
1 2 3 4 5 6 7 8
| 7 - $ rk[7]=0 0 - a rk[0]=1 2 - a rk[2]=1 5 - a rk[5]=1 1 - b rk[1]=2 3 - b rk[3]=2 4 - c rk[4]=3 6 - d rk[5]=4
|
之後要怎麼從長度$2^k$轉移成$2^{k+1}$倍呢?
把他們給變成一個pair(rk[$i$], rk[$i + 2^k$])。
1 2 3 4 5 6 7 8
| 7 - $a rk[7]=0 (0, 1) // (rk[7], rk[0]) 0 - ab rk[0]=1 (1, 2) 2 - ab rk[2]=1 (1, 2) 5 - ab rk[5]=1 (1, 3) 1 - ba rk[1]=2 (2, 1) 3 - bc rk[3]=2 (2, 3) 4 - ca rk[4]=3 (3, 1) 6 - d$ rk[6]=3 (4, 0)
|
然後使用std::sort排序他們。把排序的結果寫進rk
1 2 3 4 5 6 7 8
| 7 - $a rk[7]=0 (0, 1) 0 - ab rk[0]=1 (1, 2) 2 - ab rk[2]=1 (1, 2) 5 - ad rk[5]=2 (1, 3) 1 - ba rk[1]=3 (2, 1) 3 - bc rk[3]=4 (2, 3) 4 - ca rk[4]=5 (3, 1) 6 - d$ rk[6]=6 (4, 0)
|
再蓋pair
1 2 3 4 5 6 7 8
| 7 - $aba rk[7]=0 (0, 3) //(rk[7], rk[1]) 0 - abab rk[0]=1 (1, 1) 2 - abca rk[2]=1 (1, 5) 5 - ad$a rk[5]=2 (2, 0) 1 - babc rk[1]=3 (3, 4) 3 - bcad rk[3]=4 (4, 2) 4 - cad$ rk[4]=5 (5, 6) 6 - d$ab rk[6]=6 (6, 1)
|
然後排序… 做logn次
最後得出的順序70251346,扣掉最左邊新加的,0251346即是答案。
複雜度:logn次中,每次排序都$\mathcal{O}(nlogn)$,所以是$\mathcal{O}(nlog^2n)$。
優化
- 因為只是要對pair來sort,且pair的值域只有$[0,n]$,因此可以先排序右邊再排序左邊(radix sort),複雜度$O(nlogn)$。
- 考慮到pair(rk[$i$], rk[$i+2^k$])中,rk[$i$]已經排序過了,所以可以改成pair(rk[$i-2^k$], rk[$i$]),這樣只需要排序左邊(counting sort),常數優化。
Code
回傳的tmp沒拔掉最左邊新加的,然後我直接拿TIOJ-1497的code來用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
| #include <bits/stdc++.h> using namespace std; #define EmiliaMyWife ios::sync_with_stdio(0); cin.tie(NULL);
void csort(vector<int> &tmp, vector<int> &rk) { int n = tmp.size(); vector<vector<int>> has(n); for(int a : tmp) has[rk[a]].push_back(a); int it = 0; for(int i = 0; i < n; i++) for(int &a : has[i]) tmp[it++] = a; }
vector<int> solve(string &s) { s.push_back(0); int n = s.size(), k = __lg(n) + 1; vector<int> rk(n), tmp(n), nrk(n); for(int i = 0; i < n; i++) tmp[i] = i; sort(tmp.begin(), tmp.end(), [&](auto a, auto b) { return s[a] < s[b]; }); for(int i = 0, cnt = 0; i < n; i++) { rk[tmp[i]] = cnt; if(i + 1 < n && s[tmp[i]] != s[tmp[i + 1]]) cnt++; } for(int j = 0; j < k; j++) { for(int i = 0; i < n; i++) tmp[i] = (tmp[i] - (1 << j) % n + n) % n; csort(tmp, rk); for(int i = 0, cnt = 0; i < n; i++) { nrk[tmp[i]] = cnt; auto cur = make_pair(rk[tmp[i]], rk[(tmp[i] + (1 << j)) % n]); auto nxt = make_pair(rk[tmp[i + 1]], rk[(tmp[i + 1] + (1 << j)) % n]); if(i + 1 < n && cur != nxt) cnt++; } rk = nrk; } return tmp; }
signed main() { EmiliaMyWife
string s; getline(cin, s); auto w = solve(s); for(int i = 1; i < s.size(); i++) cout << w[i] << '\n';
return 0; }
|
習題