定義:對於字符串\(s\),若\(s\)的最小後綴爲其自己,那麼稱\(s\)爲Lyndon串c++
等價性:\(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串spa
證實:
要證實\(uv\)爲Lyndon串只需證實\(uv\)自己爲其最小後綴,
咱們能夠把全部的後綴分爲兩類,一類是由\(u\)的後綴加上\(v\)串的來,這部分的相對大小不會改變。
另外一類是\(v\)串的後綴,由於\(v\)自己也是Lyndon串,咱們只需證實\(v > uv\),由於\(v > u\),顯然成立code
證實:
設\(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\)
矛盾ip
(下面內容抄襲並補充自參考資料2)字符串
該算法能夠在\(O(n)\)的時間內求出串\(s\)的Lyndon分解get
測試地址it
引理2:若字符串\(v\)和字符\(c\)知足\(vc\)是某個Lyndon串的前綴,則對於字符\(d>c\)有\(vd\)是Lyndon串class
證實:和上面一樣的思路,對於\(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; }