Lyndon Word學習筆記


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

金策—字符串選講


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM