Lydon 分解与最小表示法
我们定义一个串是 \(\text{Lyndon}\) 串,当且仅当这个串的最小后缀就是这个串本身。
该命题等价于这个串是它的所有循环表示中字典序最小的。
引理 1:如果 \(u\) 和 \(v\) 都是 \(\text{Lyndon}\) 串并且 \(u<v\),则 \(uv\) 也是 \(\text{Lyndon}\) 串。
证明:
1、若 \(\operatorname{len}(u)\ge\operatorname{len}(v)\)
这时,\(u\) 和 \(v\) 这两个串在 \(\operatorname{len}(v)\) 之前就出现了不同的字符,所以有 \(v>uv\),又因为 \(v\) 是 \(\text{Lyndon}\) 串,所以 \(v\) 的所有后缀都大于 \(v\),所以 \(uv\) 的所有后缀都大于 \(uv\),故 \(uv\) 是 \(\text{Lyndon}\) 串。
2、若 \(\operatorname{len}(u)<\operatorname{len}(v)\)
若 \(u\) 不是 \(v\) 的前缀,那么有 \(v>uv\),得证。
若 \(u\) 是 \(v\) 的前缀,那么如果 \(v<uv\),则必有 \(v[\operatorname{len}(u)+1:]<v\)(也就是各自去掉了前 \(|u|\) 个字符),矛盾。
我们定义一个串 \(S\) 的 \(\text{Lyndon}\) 分解为一个字符串序列 \(A_1,A_2,\dots,A_m\),满足:
\(\forall i \in [1,m]∀i∈[1,m]\),满足 A_i是 \(\text{Lyndon}\) 串。
\(\forall i \in [1,m-1]∀i∈[1,m−1]\),满足 \(A_i\ge A_{i+1}\)
可以证明这种划分存在且唯一。
存在性证明:
初始令 \(m=|S|\) 并且 \(A_i=S[i]\),然后每次不断找到 \(A_i<A_{i+1}\) 并且合并为一个串。最后一定能使得所有的 \(A_i\ge A_{i+1}\)
实际上,通过这个证明,我们可以对其建出 \(SAM\) ,再通过 \(O(1)\) 求 \(parent\) 树上的 \(lca\) 比较两个后缀的大小即可在时间空间复杂度均视为 \(O(n)\) 的情况下求出一个字符串的 \(Lydon\) 分解。
引理2:若字符串 \(v\) 和字符 \(c\) 满足 \(vc\) 是某个 \(\text{Lyndon}\) 串的前缀,则对于字符 \(d>c\) 有 \(vd\) 是 \(\text{Lyndon}\) 串。
证明:
设该 \(\text{Lyndon}\) 串为 \(v+c+t\)。
则 \(\forall i \in [2,|v|],v[i:]+c+t>v+c+t\),也就是说 \(v[i:]+c\ge v\)。
所以 \(v[i:]+d>v[i:]+c\ge v\)。
同时因为 \(c>v[1]\),我们有 \(d>c>v[1]\)。
故 \(v+d\) 是一个 \(\text{Lyndon}\) 串。
Duval 算法
这个算法可以在 \(O(n)\) 时间复杂度,\(O(1)\) 空间复杂度内求出一个串的 \(\text{Lyndon}\) 分解。
该算法中我们仅需维护三个变量 \(i,j,k\)。
维持一个循环不变式:
\(s[:i-1]=s_1s_2\cdots s_g\) 是固定下来的分解,也就是 \(\forall l\in[1,g],s_l\) 是 \(\text{Lyndon}\) 串且 \(s_l>s_{l+1}\)
\(s[i,k−1]=t^h+v\times [h>1]\) 是没有固定的分解,满足 \(t\) 是 \(\text{Lyndon}\) 串,且 \(v\) 是 \(t\) 的可为空的不等于 \(t\) 的前缀,且有 \(s_g>s[i,k-1]\)
如下图:

当前读入的字符是 \(s[k]\),令 \(j=k-|t|\)。
分三种情况讨论:
当 \(s[k]=s[j]\) 时,直接 \(k\leftarrow k+1,j\leftarrow j+1\),周期 \(k-j\) 继续保持。
当 \(s[k]>s[j]\) 时,由引理 2 可知 \(v+s[k]\) 是 \(\text{Lyndon}\) 串,由于 \(\text{Lyndon}\) 分解需要满足 \(s_i\ge s_{i+1}\),所以不断向前合并,最终整个 \(t^h+v+s[k]\) 形成了一个新的 \(\text{Lyndon}\) 串。
当 \(s[k]<s[j]\) 时,\(t^h\) 的分解被固定下来,算法从 \(v\) 的开头处重新开始。
复杂度分析:\(i\) 只会单调往右移动,同时 \(k\) 每次移动的距离不会超过 \(i\) 移动的距离,所以时间复杂度是 \(O(n)\) 的。
【模板】Lyndon 分解
点击查看代码
#include<bits/stdc++.h>
using namespace std;
char s[5000005];
int n,ans;
int main(){
scanf("%s",s+1);
n=strlen(s+1);
int i=1;
while(i<=n){
int j=i,k=i+1;
while(k<=n&&s[k]>=s[j]){
if(s[k]>s[j])j=i;
else j++;k++;
}
while(i<=j){
ans^=(i+k-j-1);
i+=k-j;
}
}printf("%d",ans);
return 0;
}
最小表示法
一个字符串的最小表示定义为其所有循环同构中字典序最小的串。
最小表示法可以使用 \(\text{Lyndon}\) 分解求出。
对于长度为 \(n\) 的字符串 \(s\),设 \(t=s+s\),对 \(t\) 进行 \(\text{Lyndon}\) 分解,找到首字符位置 \(\le n\) 且最大的 \(\text{Lyndon}\) 串,这个串的首字符即最小表示法的首字符。
【模板】最小表示法
点击查看代码
#include<bits/stdc++.h>
using namespace std;
int s[5000005];
int n,ans;
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++)scanf("%d",&s[i]);
for(int i=1;i<=n;i++)s[i+n]=s[i];
int i=1;
while(i<=n){
int j=i,k=i+1;
while(k<=2*n&&s[k]>=s[j]){
if(s[k]>s[j])j=i;
else j++;k++;
}
while(i<=j){
i+=k-j;
if(i<=n)ans=i;
}
}
for(int i=0;i<n;i++)printf("%d ",s[i+ans]);
return 0;
}
[JSOI2019]节日庆典
点击查看代码
#include<bits/stdc++.h>
using namespace std;
int n;
char s[3000005];
int z[3000005];
void exkmp(){
int l=0,r=0;z[1]=n;
for(int i=2;i<=n;i++){
if(r>i)z[i]=min(z[i-l+1],r-i+1);
while(i+z[i]<=n&&s[i+z[i]]==s[z[i]+1])z[i]++;
if(i+z[i]-1>r)l=i,r=i+z[i]-1;
}
return ;
}
vector<int>now,nxt;
int main(){
scanf("%s",s+1);
n=strlen(s+1);exkmp();
for(int i=1;i<=n;i++){
now.push_back(i);
nxt.clear();
for(int j=0;j<now.size();j++){
int p=now[j];
bool flag=1;
while(!nxt.empty()){
int q=nxt.back();
if(s[i]>s[q+i-p])flag=0;
if(s[i]>=s[q+i-p])break;
nxt.pop_back();
}
if(flag&&(nxt.empty()||(i-p+1<p-nxt.back())))nxt.push_back(p);
}
now=nxt;
int pos=now[0];
for(int j=1;j<now.size();j++){
int x=now[j],k=pos+i-x;
if(z[k+1]>=i-k){
register int l=i-k;
if(z[l+1]<x-l-1&&s[l+z[l+1]+1]<s[z[l+1]+1])pos=x;
}
else if(s[z[k+1]+1]<s[k+z[k+1]+1])pos=x;
}
printf("%d ",pos);
}
return 0;
}
Lydon 分解与最小表示法的更多相关文章
- HDU 4162 Shape Number (最小表示法)
题意:给你一串n个数,求出循环来看一阶差的最小字典序:数字串看成一个顺时针的环,从某一点开始顺时针循环整个环,保证字典序最小就是答案 例如给你 2 1 3 就会得到(1-2+8 注意题意负数需要加8) ...
- POJ 1635 树的最小表示法/HASH
题目链接:http://poj.org/problem?id=1635 题意:给定两个由01组成的串,0代表远离根,1代表接近根.相当于每个串对应一个有根的树.然后让你判断2个串构成的树是否是同构的. ...
- HDU 2609 最小表示法
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2609 题意:给定n个循环链[串],问有多少个本质不同的链[串](如果一个循环链可以通过找一个起点使得和 ...
- HDU 4162 最小表示法
题目:http://acm.hdu.edu.cn/showproblem.php?pid=4162 题意:给定一个只有0-7数字组成的串.现在要由原串构造出一个新串,新串的构造方法:相邻2个位置的数字 ...
- POJ 1509 最小表示法
题目链接:http://poj.org/problem?id=1509 题意:给定一个字符串,求一个起点使字符串从该起点起的字符串字典序最小[题目的字符串起点从1开始] 思路:最小表示法模板题 #de ...
- UVA 1314 最小表示法
题目链接:http://acm.hust.edu.cn/vjudge/problem/viewProblem.action?id=36117 题意:给定长度为n的字符串,求一个起点使字符串从该起点起的 ...
- hdu String Problem(最小表示法入门题)
hdu 3374 String Problem 最小表示法 view code#include <iostream> #include <cstdio> #include &l ...
- hdu5442(2015长春赛区网络赛1006)后缀数组+KMP /最小表示法?
题意:给定一个由小写字母组成的长度为 n 的字符串,首尾相连,可以从任意一个字符开始,顺时针或逆时针取这个串(长度为 n),求一个字典序最大的字符串的开始字符位置和顺时针或逆时针.如果有多个字典序最大 ...
- 【KMP】【最小表示法】NCPC 2014 H clock pictures
题目链接: http://acm.csu.edu.cn/OnlineJudge/problem.php?id=1794 题目大意: 两个无刻度的钟面,每个上面有N根针(N<=200000),每个 ...
随机推荐
- 公私钥 SSH 数字证书
公私钥 SSH 数字证书 小菜鸟今天买了华为云一台服务器,在使用公私钥远程登录服务器的时候,忘记了相关公钥私钥的原理和一些应用了,今天复习一波做个记录. 相关概念 公钥:公钥用来给数据加密,用公钥加密 ...
- Nginx下载文件指定文件名称
配置 location ^~/TEMP/ { alias/share/files/; if ($request_uri ~* ^.*\/(.*)\.(txt|doc|pdf|rar|gz|zip|do ...
- c++:-1
C++第一部分介绍基础:c++:-0,本节介绍C++中函数使用. 函数 函数调用 调用函数需要先声明函数原型 嵌套调用: 参数传递 在函数被调用时才分配形参的存储单元 实参可以是常量.变量或表达式 实 ...
- C#/VB.NET 在Excel单元格中应用多种字体格式
在Excel中,可对单元格中的字符串设置多种不同样式,通常只需要获取到单元格直接设置样式即可,该方法设置的样式会应用于该单元格中的所有字符.如果需要对单元格中某些字符设置样式,则可以参考本文中的方法. ...
- .NetCore Hangfire任务计划
安装Hangfire 新建ASP.NET Core空 项目,.Net Core版本3.1 往*.csproj添加包引用,添加新的PackageReference标记.如下所示.请注意,下面代码段中的版 ...
- js--promise、async 和 await 相关知识总结
前言 promise 是前端开发人员必须掌握的知识点,本文来总结一下相关学习笔记. 正文 1.什么是prommise,promise 解决了什么问题 a.promise 是什么 Promise 是承诺 ...
- Vulnhub-napping
1.信息收集 先用arp-scan探测出靶机地址 arps-scan -l 显然靶机地址 10.1.1.106,然后利用nmap进行详细信息收集 nmap -A 10.1.1.106 可以看到,目标的 ...
- zabbix 添加监控交换机温度item
首先需要获取到交换机温度对应的OID,可以官方文档进行查询(多为私有OID),以盛科为例 官方文档查询到温度节点对于的OID为 10.0.3.102 1.3.6.1.4.1.27975.37.1.3. ...
- 前后端分离,SpringBoot如何实现验证码操作
验证码的功能是防止非法用户恶意去访问登录接口而设置的一个功能,今天我们就来看看在前后端分离的项目中,SpringBoot是如何提供服务的. SpringBoot版本 本文基于的Spring Boot的 ...
- CentOS下Python管理
一.升级Python 查看系统版本 cat /etc/redhat-release CentOS Linux release 7.4.1708 (Core) 查看Python版本 python -V ...