Lyndon 分解

样例

样例输入 1

ababa

样例输出 1

2 4 5

样例输入 2

bbababaabaaabaaaab

样例输出 2

1 2 4 6 9 13 18

样例输入 3

azAZ0129

样例输出 3

2 4 8

数据范围与提示

\(1\le |s| \le 2^{20}\)

OZY的题解

冷门东西,但是今天考到了,做个记录。

记号

\(s[l : r]\) 表示字符串\(s\) 从第\(l\) 个字符到第\(r\) 个字符的子串(从\(1\) 开始标号),\(|s|\) 表示\(s\) 的长度。

当\(l = 1\) 时\(s[l : r]\) 简写为\(s[: r]\) ,表示\(s\) 的一个前缀。当\(r = |s|\) 时\(s[l : r]\) 简写为\(s[l :]\) ,表示\(s\) 的一个后缀。

\(st, s +t\) 表示两个字符串\(st\) 的拼接,\(s^k\) 表示\(k\) 个\(s\) 拼起来,特别地,\(s^{\infty}\) 表示\(s\) 的无限循环。

定义

Lyndon 串:如果一个串\(s\) 满足\(s = \min\{s[i :]|1 \le i \le |s|\}\) 那么我们称串\(s\) 为Lyndon 串。定义字符串的大小关系就是字典序的大小关系

性质

当\(u,v\)均为Lyndon Words,且\(u<v\),那么\(uv\)也是一个Lyndon Words。

证明还是比较显然的,这里就不证了

Lyndon 划分

对于一个字符串\(s\),如果一个划分将它分成若干个串\(s=p_1+p_2+p_3+\dots+p_n\),使得每个\(p\)都是Lyndon Words,且\(p_i\ge p_{i+1}\),则这个划分是Lyndon 划分

可以发现,一个字符串,一定存在一种Lyndon 划分,证明可以用构造法来证明。

一开始先所有\(p\)设为单个字母。显然,这是满足第一个条件的,只需要再满足递减的关系就可以了。

可以发现若\(p_i<p_{i+1}\),它们合起来也是一个Lyndon Words。

并且可以发现,对于一个串,他的Lyndon 划分是唯一的。

算法

目的是求出\(r[i]\),表示第\(i\)个字符所属Lyndon Words的右端点的下一个位置。

就是维护类似单调栈的东西就可以了。单调栈内维护的是属于同一Lyndon Words的节点,换句话说如果不满足字典序的单调递增,就要清空。发现这就是维护定义……很显然啊。

复杂度瓶颈在于比较后缀大小,用后缀树(DC3后缀数组+笛卡尔树)和±1RMQ即可\(O(n)\)。这里只给出不能AC的hash做法,\(O(n\log n)\)。

当然这题还有\(O(n)\)的Duval算法,但是我觉得没必要学。

#include<bits/stdc++.h>
#define co const
#define il inline
using namespace std;
typedef unsigned long long ULL; co int N=(1<<20)+10;
co ULL base=131;
char str[N];int n;
ULL pw[N],hs[N]; il ULL calc(int l,int r){
return hs[r]-hs[l-1]*pw[r-l+1];
}
int lcp(int x,int y){ // str[x:],str[y:]
int l=0,r=n-max(x,y)+1;
while(l<r){
int mid=(l+r+1)>>1;
if(calc(x,x+mid-1)==calc(y,y+mid-1)) l=mid;
else r=mid-1;
}
return l;
}
il bool cmp(int x,int y){ // str[x:]<str[y:]
int len=lcp(x,y);
if(len==n-max(x,y)+1) return x>y; // partition by >=
return str[x+len]<str[y+len];
} int r[N],st[N],top;
int main(){
scanf("%s",str+1),n=strlen(str+1);
pw[0]=1;
for(int i=1;i<=n;++i){
pw[i]=pw[i-1]*base;
hs[i]=hs[i-1]*base+str[i];
}
for(int i=1;i<=n;++i){
while(top&&cmp(i,st[top])) r[st[top--]]=i;
st[++top]=i;
}
while(top) r[st[top--]]=n+1;
for(int i=1;i<=n;i=r[i]) printf("%d ",r[i]-1);
return 0;
}

LOJ129 Lyndon 分解的更多相关文章

  1. HDU - 6761 Minimum Index (字符串,Lyndon分解)

    Minimum Index 题意 求字符串所有前缀的所有后缀表示中字典序最小的位置集合,最终转换为1112进制表示.比如aab,有三个前缀分别为a,aa,aab.其中a的后缀只有一个a,位置下标1:a ...

  2. 知识点简单总结——Lyndon分解

    知识点简单总结--Lyndon分解 Lyndon串 定义:一个字符串的最小后缀就是整个串本身. 等效理解:这个串为其所有循环表示中最小的. Lyndon分解 定义:将字符串分割为 $ s_{1} s_ ...

  3. Lyndon 相关的炫酷字符串科技

    浅谈从 Lyndon Words 到 Three Squares Lemma By zghtyarecrenj 本文包括:Lyndon Words & Significant Suffixes ...

  4. Lyndon Word学习笔记

    Lyndon Word 定义:对于字符串\(s\),若\(s\)的最小后缀为其本身,那么称\(s\)为Lyndon串 等价性:\(s\)为Lyndon串等价于\(s\)本身是其循环移位中最小的一个 性 ...

  5. Lyndon words学习笔记

    Lyndon words 定义: 对于一个字符串\(S\),若\(S\)的最小后缀是其本身,则\(S\)为一个\(lyndon\)串; 记为\(S\in L\); 即: \[S \in L \begi ...

  6. Lyndon Word相关

    Lyndon Word 定义 对于字符串 \(S\),若 \(S\) 的最小后缀为其本身,那么称 \(S\) 为 \(\text{Lyndon}\) 串(\(\text{Lyndon Word}\)) ...

  7. Lydon 分解与最小表示法

    我们定义一个串是 \(\text{Lyndon}\) 串,当且仅当这个串的最小后缀就是这个串本身. 该命题等价于这个串是它的所有循环表示中字典序最小的. 引理 1:如果 \(u\) 和 \(v\) 都 ...

  8. ZROI 暑期高端峰会 A班 Day3 字符串

    FBI Warning:本文含有大量人类的本质之一 后缀树 反正后缀树就是反串的后缀自动机的 Parent 树,就不管了. 然而 SAM 也忘了 好的假装自己会吧--dls 后缀自动机 大概记得,不管 ...

  9. 2019暑期金华集训 Day3 字符串

    自闭集训 Day3 字符串 SAM 考虑后缀树. SAM的parent树是反串的后缀树,所以后面加一个字符的时候相当于往串前面加一个字符,恰好多出了一个后缀. 于是可以以此来理解SAM. 每一条路径对 ...

随机推荐

  1. python is 和 == 区别(8)

    在python中is和==都说常用的运算符之一,主要用于检测两个变量是否相等,返回True或者False,具体区别在哪呢? 一.前言 在讲解is和==区别直接先讲解一下内置函数id(),其实在文章 p ...

  2. LeetCode 572. 另一个树的子树(Subtree of Another Tree) 40

    572. 另一个树的子树 572. Subtree of Another Tree 题目描述 给定两个非空二叉树 s 和 t,检验 s 中是否包含和 t 具有相同结构和节点值的子树.s 的一个子树包括 ...

  3. TemplateDoesNotExist

    路径问题,可能是没有配置这一行:

  4. nginx acces.log日志分析

    1,统计各访问IP的总数 awk '{if($9>0 && $9==200 && substr($6,2)== "GET") a[$1]++} ...

  5. Python第六章 面向对象

    第六章 面向对象 1.面向对象初了解 ​ 面向对象的优点: ​ 1.对相似功能的函数,同一个业务下的函数进行归类,分类 ​ 2.类是一个公共的模板,对象就是从具体的模板中实例化出来的,得到对象就得到一 ...

  6. PAT(B) 1067 试密码(Java)

    题目链接:1067 试密码 (20 point(s)) 题目描述 当你试图登录某个系统却忘了密码时,系统一般只会允许你尝试有限多次,当超出允许次数时,账号就会被锁死.本题就请你实现这个小功能. 输入格 ...

  7. DNS欺诈的三种简单方法总结

    使用arpspoof.ettercap以及driftnet的简单组合. ①arpsppof+driftnet arpspoof -i eth0 -t 目标ip 目标网关 driftnet -i eth ...

  8. Jenkins + GitLab + SpringBoot 实现持续集成脚本

    Linux脚本 #!/bin/bash jar_name=hq-api.jar cd /usr/local/app/hq-api echo "Stopping SpringBoot Appl ...

  9. CF778D Parquet Re-laying 构造

    传送门 如果\(2 \not\mid M\),就把两个图折一下,把\(N\ M\)互换,这样就可以保证\(2 \mid M\). 因为操作可逆,所以我们可以选择一个中间状态,把起始和终点状态都变成这个 ...

  10. .net core使用ocelot---第四篇 限流熔断

    简介 .net core使用ocelot---第一篇 简单使用 .net core使用ocelot---第二篇 身份验证 .net core使用ocelot---第三篇 日志记录 前几篇文章我们陆续介 ...