zoukankan      html  css  js  c++  java
  • 【学习笔记】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<v),则(uv)也是(Lyndon)串。

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

    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)<len(v))

    (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_i<s[j])都并进去了,所以没有并进去的话,就满足每一段(s_i>s_{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_i)(Lyndon)串,所以它的后缀大于它

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

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

    (s_i'<s_i)(s_i')(s_i)的前缀,一个字符串的前缀小于等于它自己

    综合上述不等式,可以得到(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]<s[j])(t^h)的分解被固定为(h)(Lyndon)串,算法从(v)的开头重新开始。

    复杂度分析

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

    复杂度为(O (n))

    Code View

    #include<cstdio>
    #include<algorithm>
    #include<queue>
    #include<cstring>
    #include<iostream>
    #include<cstdio>
    #include<cmath>
    #include<map>
    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<'0'||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]<s[k]) j=i;//合并 
    			else j++;
    			k++; 
    		}
    		while(i<=j)
    		{
    			ans=ans^(i+k-j-1);
    			i+=k-j;
    		}
    	}
    	printf("%d
    ",ans);
        return 0;
    }
    

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

  • 相关阅读:
    文件操作方法大全以及文件打开的其他一些模式sys.stdout.write()就是标准输出到你当前的屏幕 sys.stdout.flush()把内存立即显示到您当前的屏幕
    文件操作之循环
    python中的enumerate函数用于遍历序列中的元素以及它们的下标
    文件操作open,r,w,a三种模式
    盖得化工--采集所有公司详细信息
    盖得化工--采集二级网址的公司详细信息
    [python网络编程]使用scapy修改源IP发送请求
    盖得化工----requests/bs4---采集二级网址
    盖得化工--selenium翻页测试
    盖得化工网——翻页测试---页码框输入页码
  • 原文地址:https://www.cnblogs.com/lyttt/p/13466717.html
Copyright © 2011-2022 走看看