Lyndon Word學習筆記

自為風月馬前卒發表於2018-12-30

Lyndon Word

定義:對於字串(s),若(s)的最小字尾為其本身,那麼稱(s)為Lyndon串

等價性:(s)為Lyndon串等價於(s)本身是其迴圈移位中最小的一個

性質

任意字串(s)都可以分解為(s = s_1 s_2 dots s_k),其中(forall s_i)為Lyndon串且(s_i geqslant s_{i +1})。且這種分解方法是唯一的

  • 存在性

引理1:若(u, v)為Lyndon串,且(u < v),那麼(uv)為Lyndon串

證明:
要證明(uv)為Lyndon串只需證明(uv)本身為其最小字尾,
我們可以把所有的字尾分為兩類,一類是由(u)的字尾加上(v)串的來,這部分的相對大小不會改變。
另一類是(v)串的字尾,因為(v)本身也是Lyndon串,我們只需證明(v > uv),因為(v > u),顯然成立

  • 唯一性

證明:
(pre(s, i))表示串(s)(s[1 dots i])所代表的字首
若有兩種方案,取第一次不同的位置,設(|s_i| > |s`_i|)
(s_i = s`_i s`_{i + 1} dots s`_{k} pre(s_{k + 1}, l))
反證法。根據定義,(s_i < pre(s`_{k + 1}, l) leqslant s`_{k + 1} leqslant s`_i < s_i)
矛盾

Duval演算法

(下面內容抄襲並補充自參考資料2)

該演算法可以在(O(n))的時間內求出串(s)的Lyndon分解

測試地址

引理2:若字串(v)和字元(c)滿足(vc)是某個Lyndon串的字首,則對於字元(d>c)(vd)是Lyndon串

證明:和上面同樣的思路,對於(d)之前的字尾相對大小不會改變,之後的字尾只會變大

該演算法中我們僅需維護三個變數(i, j, k)

(s[1..i – 1] = s_1 s_2 dots s_g)是固定下來的分解,也就是(forall l in [1, g] s_l)是Lyndon串且(s_l > s_{l + 1})

(s[i .. k – 1] = t_1 t_2 dots t_h v(h > 1)) 是沒有固定的分解,滿足(t_1)是Lyndon串,且(t_1 = t_2 = dots = t_h)(v)(t_h)的(可為空的)真字首,且有(s_g > s[i .. k – 1])

當前讀入的字元是(s[k]),令(j = k – |t_1|)

分三種情況討論

  • (s[k] = s[j])時,週期(k – j)繼續保持

  • (s[k] > s[j])時,合併得到(t_1 <- t_1 t_2 dots t_h v s[k])是Lyndon串

  • (s[k] < s[j])時,(t_1, t_2, dots, t_h)的分解被固定下來,演算法從(v)的開頭處重新開始

#include<bits/stdc++.h>
using namespace std;
const int MAXN = (1 << 21) + 1;
char s[MAXN];
int main() {
    scanf("%s", s + 1);
    int N = strlen(s + 1), j, k;
    for(int i = 1; i <= N;) {
        j = i; k = i + 1;
        while(k <= N && s[j] <= s[k]) {
            if(s[j] < s[k]) j = i;
            else j++;
            k++;
        }
        while(i <= j) {
            printf("%d ", i + k - j - 1);
            i += k - j;
        }
    }
    return 0;
}

參考資料

Lyndon word

金策—字串選講

相關文章