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;
}