HDU - 6761 Minimum Index (字串,Lyndon分解)
阿新 • • 發佈:2020-07-22
Minimum Index
題意
求字串所有字首的所有後綴表示中字典序最小的位置集合,最終轉換為1112進製表示。比如aab,有三個字首分別為a,aa,aab。其中a的字尾只有一個a,位置下標1;aa有兩個字尾,字典序最小的是a,下標為2;aab有三個字尾,字典序最小的是aab,下標是1。答案為 \(1*(1112)^2+2*(1112)^1+1*(1112)^0\)
字串長度1e6
分析
再求字串的最小表示法中,有一個叫做Lyndon分解的求法,yndon分解可以使用Duval演算法。詳情可以參考 oi-wiki。
設\(d[j]\) 為字首 j 的字典序最小字尾的起始位置,i, j, k 指標與oi-wiki中介紹的一致。對於下面三種情況討論d[j]的求解
\(j - k\) 為 近似Lyndon串字首的迴圈節長度。
- \(s[j] == s[k]\), 那麼d[j] = d[k] + (j - k); 本質上是取了 j 所在迴圈節的開頭位置。(\(s=www\overline{w}\) 中 \(\overline{w}\) 的開頭)
- \(s[j] > s[k]\),那麼 d[j] = i; 當前\(s[i..j]\) 是一個Lyndon串,所以d[j] = i;
- \(s[j] < s[k]\),Duval演算法中會重新處理 j 所在的這一段(\(s=www\overline{w}\) 中 \(\overline{w}\)), i會被置為這一段的開頭,繼續後面的分解過程。這裡有一個特殊情況需要考慮,如果 \(j == k + 1\)
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int inf = 0x3f3f3f3f; #define dbg(x...) do { cout << "\033[32;1m" << #x <<" -> "; err(x); } while (0) void err() { cout << "\033[39;0m" << endl; } template<class T, class... Ts> void err(const T& arg,const Ts&... args) { cout << arg << " "; err(args...); } const int N = 1e6 + 5; const int mod = 1e9 + 7; char s[N]; ll d[N], n; int main(){ int T;scanf("%d", &T); while(T--){ scanf("%s", s + 1); n = strlen(s + 1); int i = 1; d[1] = 1; while(i <= n) { int j = i + 1, k = i; while(j <= n && s[k] <= s[j]) { if(s[k] == s[j]){ d[j] = d[k] + (j - k); k ++; } else { d[j] = i; k = i; } j ++; } d[j] = j; // 當 k == j - 1 時,必須有這一條。因為下面的迴圈結束後,i = k + 1 也就是 j,接下來的大迴圈不會在處理當前的 j, 這次 j 是被當做lyndon分解串的一個起點對待的。 while(i <= k) i += j - k; } ll res = 0; for(int i = n;i>=1; i --){ res = res * 1112 + d[i]; res %= mod; } printf("%lld\n", res); } return 0; }