1. 程式人生 > 實用技巧 >HDU - 6761 Minimum Index (字串,Lyndon分解)

HDU - 6761 Minimum Index (字串,Lyndon分解)

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串字首的迴圈節長度。

  1. \(s[j] == s[k]\), 那麼d[j] = d[k] + (j - k); 本質上是取了 j 所在迴圈節的開頭位置。(\(s=www\overline{w}\)\(\overline{w}\) 的開頭)
  2. \(s[j] > s[k]\),那麼 d[j] = i; 當前\(s[i..j]\) 是一個Lyndon串,所以d[j] = i;
  3. \(s[j] < s[k]\),Duval演算法中會重新處理 j 所在的這一段(\(s=www\overline{w}\)\(\overline{w}\)), i會被置為這一段的開頭,繼續後面的分解過程。這裡有一個特殊情況需要考慮,如果 \(j == k + 1\)
    ,那麼 j 就是下一次分解的開頭, i 會被置為 j,所以要手動將d[j] = j。
#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;
}