字符串算法 金策_【学习笔记】Lyndon Word

定义

若一个字符串\(s\)的最小后缀是它自己,我们称其为\(Lyndon\)串。

等价定义:若\(s\)是其所有循环重构串中字典序最小的串,则\(s\)是\(Lyndon\)串。

\(Lyndon\)分解

任意字符串\(s\),都可以唯一分解成\(s=s_1s_2...s_k\),其中\(\forall s_i\)为\(lyndon\)串,且\(s_i≥s_{i+1}\)

存在性证明

先来看一个引理:

引理1:

如果\(u\),\(v\)都是\(Lyndon\)串,并且字典序\(u

(感觉比较显然吧,但还是证明一下

1.\(len(u)≥len(v)\)时

因为\(v>u\),所以\(v\)>\(uv\),又因为\(v\)是\(Lyndon\)串,所以\(v\)的所有非自己的后缀都大于\(v\),所以开头在\(v\)的部分的后缀都大于\(uv\)。因为\(u\)也是\(Lyndon\)串,所以\(u\)的所有后缀都大于\(u\),\(uv\)的开头在\(u\)部分的后缀也就大于\(uv\)(在\(u\)部分的比较就已经大于)

2.\(len(u)

若\(u\)不是\(v\)的前缀,那么在\(len(u)\)之前就有\(v>u\),所以\(v>uv\),同上可证。

若\(u\)是\(v\)是的前缀,那么\(v>uv\),同上可证。

有了引理之后再来证明。

首先,\(s\)中的每一个单个字符都是一个\(Lyndon\)串,初始时每段都只有一个字符。

从左往右开始合并,左边已经合并了的字符串\(s_i\)大于当前字符\(s[j]\)的话就把\(s[j]\)并入\(s_i\),根据引理1可得这样合并后分出来的每一段都是\(Lyndon\)串。

而每次比较的时候\(s_is_{i+1}\)

唯一性证明

(其实这个也比较显然吧,根据上面我们是能并就并了,没有其他可操作的空间,所以就只有一种

这个我们用反证法。

假设对于一个字符串\(s\)有两种\(Lyndon\)分解

我们记第一次不同的位置为\(i\),设\(len(s_i)>len(s_{i}')\)

设\(s_i=s_{i}'s_{i+1}'...s_{k}'s_{k+1}'[1...l]\)

\(s'_{k+1}[1...l]\)指第\(k+1\)段串中的前缀\(1..l\)

可以得到以下关系:

\(s_i

\(s_{k+1}’[1...l]≤s_{k+1}’\):一个字符串的前缀小于等于它自己

\(s_{k+1}’≤s_i'\):\(Lyndon\)分解中,前面的段的字典序大于后面的段的字典序。

\(s_i'

综合上述不等式,可以得到\(s_i

\(Duval\)算法

是一种在\(O(n)\)时间复杂度之内求出一个串的\(Lyndon\)分解的算法。

引理2:

若字符串\(v\)和字符\(c\)满足\(vc\)是某个\(Lyndon\)串的前缀,则对于字符\(d>c\)有\(vd\)是\(Lyndon\)串。

(觉得还是可以感性理解

证明:

设\(Lyndon\)串为\(vct\)

根据\(Lyndon\)串的性质可得,\(\forall i∈[2,len(v)],v[i...len(v)]ct≥vct\).

根据\(i\)的取值范围,\(v[i.. .len(v)]\)长度最大是\(len(v)-1\),那么\(v[i...len(v)]c\)的长度小于等于\(v\)。

所以\(v[ i...len(v)]c\)要么是\(v\)的前缀(\(v[i...len(v)]ct\)在\(t\)的部分大于\(vct\)),要么大于\(v\),总之\(v[i..len(v)]c\)不可能小于\(v\),否则\(v[i...len(v)]ct≥vct\)是不会成立的。

所以可以得到:\(v[i...len(v)]c≥v\)

那么:\(v[i...len]d>v[i...len]c>v\),所以\(v[i...len]>vd\)

算法流程

在这个算法中,我们维护三个变量\(i,j,k\)

其中,\(s[1...i-1]=s_1s_2...s_g\)是已经固定下来的分解,满足每一段\(l∈[1,g-1],s_l>s_{l+1}\)

\(s[i...k-1]=t^hv,h≥1\)是没有固定的分解,并且\(t\)是\(Lyndon\)串,\(v\)是\(t\)的一个前缀(可为空)。

\(j=k-|t|\),当前扫到未处理的字符是\(s[k]\)

分三类情况讨论:

\(s[k]==s[j]\):继续往前扫,保持周期

\(s[k]>s[j]\):根据引理2,我们将\(t^hvs[k]\)合并起来,是一个\(Lyndon\)串(是向前合并,\(t\)和\(vs[k]\)合并起来,再将\(tvs[k]\)与前面的\(t\)合并(注意相等的两个串并不能用引理1进行合并,因为\(uu\)的后缀\(u\)不大于$uu $)。

这里的合并是相当于把\(t^hvs[k]\)当成一个新的\(t\),不是就把它当成\(Lyndon\)分解里的一段了,它还有可能和后面的串拼在一起形成一个新的\(Lyndon\)

\(s[k]

复杂度分析

\(i\)只会往右移,\(k\)移动的距离不超过\(i\)右移的距离(\(k\)移动\(|v|\),而\(i\)移动\(|t^hv|\))

复杂度为\(O (n)\)

Code View

#include

#include

#include

#include

#include

#include

#include

#include

using namespace std;

#define N 5000005

#define INF 0x3f3f3f3f

#define LL long long

int rd()

{

int x=0,f=1;char c=getchar();

while(c'9'){if(c=='-')f=-1; c=getchar();}

while(c>='0'&&c<='9'){x=(x<<3)+(x<<1)+(c^48); c=getchar();}

return f*x;

}

int n,ans;

char s[N];

int main()

{

scanf("%s",s+1);

n=strlen(s+1);

for(int i=1;i<=n;)

{

int j=i,k=i+1;

while(k<=n&&s[j]<=s[k])

{

if(s[j]

else j++;

k++;

}

while(i<=j)

{

ans=ans^(i+k-j-1);

i+=k-j;

}

}

printf("%d\n",ans);

return 0;

}

——注:博客参考了金策的字符串算法选讲内容

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值