用途
考慮”ababcad”
那麼就可以分成7個這樣的字串:
| 12
 3
 4
 5
 6
 7
 
 | 0 - "ababcad"1 - "babcad"
 2 - "abcad"
 3 - "bcad"
 4 - "cad"
 5 - "ad"
 6 - "d"
 
 | 
然後再把這7個字串按照ASCII碼的順序排序,變成這樣:
| 12
 3
 4
 5
 6
 7
 
 | 0 - "ababcad"2 - "abcad"
 5 - "ad"
 1 - "babcad"
 3 - "bcad"
 4 - "cad"
 6 - "d"
 
 | 
所以順序就是0251346
算法
考慮倍增
先把字串在後面補個極小字元(假設$),更後面的就亂擺(方便起見就讓它變成環吧),變成
| 12
 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"
 
 | 
可以發現大小不會變,因為有一個極小字元。
然後先取第一個字元
| 12
 3
 4
 5
 6
 7
 8
 
 | 0 - a1 - b
 2 - a
 3 - b
 4 - c
 5 - a
 6 - d
 7 - $
 
 | 
直接用std::sort $\mathcal{O}(NlogN)$後再編號進rk陣列(可以想成離散化)。
| 12
 3
 4
 5
 6
 7
 8
 
 | 7 - $ rk[7]=00 - 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$])。
| 12
 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
| 12
 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
| 12
 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來用
| 12
 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;
 }
 
 | 
習題