exkmp:
线性求出字符串的任何一个后缀与自身的lcp长度
设其为(z)数组,(z[i])表示([1,n])和([i,n])lcp的长度
考虑从前往后依次求出(z[i])
考虑维护(i+z[i]-1)最大的(j)
那么如果(i<=j+z[j]-1),考虑([i,j+z[j]-1]=[i-j+1,z[j]-1])
(z[i])就从(min(z[i-j+1],j+z[j]-i))继承过来即可
然后剩下的部分暴力往后匹配
注意你如果需要暴力往后匹配,(j+z[j]-1)必然会被当前的(i+z[i]-1)更新,每匹配一位就至少变大(1)
因为如果(min(z[i-j+1],j+z[j]-i)=z[i-j+1]),(z[i])是不会更新的,因为两者一样的部分长于(z[i-j+1]),(z[i])不会比(z[i-j+1])大
综上所述,复杂度是(O(n))的
一份含exkmp的代码code
lyndon words and runs
没有lemma的证明(有些没看懂)
lyndon串:一个满足除了本身以外的后缀,字典序都比它本身大的串
一个性质:两个lyndon串(s,t),若(s)严格小于(t),则(st)是lyndon串
一个串的lyndon分解:把一个串分解成(s_1,s_2...s_k),满足(s_1,s_2...s_k)都是lyndon串,且(s_1 geq s_2 geq ... geq s_k),这里(geq)表示字典序不小于
一个性质:一个串的lyndon分解存在且唯一
求lyndon分解:Duval's Algorithm
增量法构造lyndon分解
考虑我们当前已经有([i,|S|])的lyndon分解,要求出([i-1,|S|])的lyndon分解
考虑维护一个栈,栈中每个元素是一个lyndon串的结尾
栈中元素需要满足这些lyndon串不降
向栈中加入(i-1)
若当前(s[i:st[top]] > s[st[top]+1:st[top-1]])
则这两个可以合并,弹出栈顶
runs:定义三元组((i,j,p))是一个run,当且仅当(s_{j+1} eq s_{j-p+1},s_{i-1} eq s_{i+p-1})且(p)是([i,j])最小的出现至少两次的循环节。
求runs
求出以每个位置为左端点的最长lyndon子串
这个其实就是上面的算法里每一步添加以后的栈顶元素
runs的求解:
对于上面的每一个串([l,r]),以它的长度为周期,向左/右扩展到(l',r'),若([l',r'])的长度不小于([l,r])长度的两倍,则([l',r'])是一个runs
求解的时候要把字符大小关系反过来再求一次lyndon word和runs,才能求出全部的
模板题code