ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

The Runs Theorem 和 Lyndon Tree 学习笔记

2022-06-08 00:01:20  阅读:165  来源: 互联网

标签:Runs Word int text Tree runs Lyndon mod


定义

\(\text{Runs}\):

一个 \(\text{run}\) 是一个三元组 \(\text{r}=(l,r,p)\),表示 \(s[l,r]\) 的最小周期为 \(p\),且 \([l,r]\) 区间是极大的,要求 \(2p\leq r-l+1\) 。实数 \(\frac{r-l+1}{p}\) 称为 \(r\) 的指数。\(Runs(w)\) 表示 \(w\) 的 \(runs\) 集合,\(\rho(n)\) 表示长度为 \(n\) 的字符串含有 \(\text{runs}\) 的最大个数,\(\sigma(n)\) 表示长 \(n\) 字符串 \(\text{runs}\) 指数之和的最大值。

\(\text{Lyndon Word}\):

若一个字符串 \(s\) 满足对其任意一个严格后缀 \(t\) 都有 \(s<t\),则称其为关于 \(<\) 的一个 \(\text{Lyndon Word}\)。

在字符集 \(\Sigma\) 上定义相反的全序关系 \(<_0,<_1\) (可以理解为正反字典序),对于字符串 \(w\) 记 \(\hat{w}=w\$\),\(\$\) 满足 \(\forall a\in \Sigma,\$<_0 a,a<_1\$\) 。记 \(l_l(i)\) 表示在 \(\hat{w}\) 上从 \(i\) 开始的最长关于 \(<_l\) 的 \(\text{Lyndon Word}\) 。

\(\text{Lyndon Root}\):

令 \(\text{r}=(l,r,p)\) 是一个 \(\text{run}\),长度为 \(p\) 的一个区间 \(\lambda=[l_{\lambda},r_{\lambda}]\) 被称为 \(\text{r}\) 的一个 \(\text{Lyndon Root}\) 当且仅当 \({l\leq l_{\lambda}\leq r_{\lambda}\leq r}\),且 \(\lambda\) 是一个 \(\text{Lyndon Word}\) 。即 \(\lambda\) 是 \(\text{r}\) 长度 \(=p\) 的周期的最小循环同构。

性质

证明篇幅太长了,可以见 WC2019课件 或 command_block 的博客The "Runs" Theorem

这里只整理一下偏实用价值的最终结论。

\(\text{Lyndon Word}\) 的性质:

  • 令 \(w=u^ku'a\),\(u\) 是 \(\text{Lyndon Word}\),\(u'\) 是 \(u\) 可为空的严格前缀,\(k\geq 1\),\(a\neq w[|u'|+1]\)
    1. \(w[|u'|+1]<a\): \(w\) 是一个 \(\text{Lyndon Word}\) 。
    2. \(w[|u'|+1]>a\): \(u\) 是任何一个以 \(w\) 为前缀的字符串的最长 \(\text{Lyndon Word}\) 前缀。
  • 任何 \(\text{Lyndon Word}\) 均不存在 \(> 0\) 的 \(\text{border}\) 。
  • \(s\) 是 \(\text{Lyndon Word}\) 当且仅当 \(s=ab\),\(a<b\) 且 \(a\) 和 \(b\) 均为 \(\text{Lyndon Word}\) 。
  • 串的 \(\text{Lyndon}\) 分解:将字符串 \(s\) 分解成 \(s=s_1s_2...s_m\),满足 \(s_1..s_m\) 均为 \(\text{Lyndon Word}\) 且 \(s_1\geq s_2\geq...\geq s_m\) 。
  • 串的 \(\text{Lyndon}\) 分解必然存在且唯一。
  • 对于任意 \(\text{Lyndon Word}\) \(u\) 和 \(\text{Lyndon}\) 分解 \(f_1,..f_m\),\(u<f_1\iff uf_1..f_m<f_1..f_m\) 。
  • 对于任意字符串 \(s\) 和一个位置 \(i\),有且仅有一个 \(l\in\{0,1\}\) 满足 \(l_l(i)=[i,i]\),且 \(l_{1-l}(i)=[i,j],j>i\) 。

\(\text{Runs}\) 的性质:

  • \(\text{The Runs Theorem}\):\(\rho(n)<n,\sigma(n)\leq 3n-3\) 。

  • 对于 \(w\) 的一个 \(\text{run}\) \(\text{r}=(l,r,p)\),有且仅有一个 \(l\in\{0,1\}\) 满足 \(\hat{w}[r+1]<_l \hat{w}[r+1-p]\),所有 \(\text{r}\) 关于 \(<_l\) 的 \(\text{Lyndon Root}\) \(\lambda=[l_{\lambda},r_{\lambda}]\) 都与 \(l_{l}(l_{\lambda})\) 相等。(为 \(\text{Lyndon Word}\) 性质一的体现)

  • 定义 \(B_{\text{r}}\) 为 \(\text{r}\) 的所有关于 \(<_l\) 的 \(\text{Lyndon Root}\) 的集合除去开头 \(l\) 开始的部分,\(\hat{w}[r+1]<_l \hat{w}[r+1-p]\)。对于任意两个 \(\text{runs}\;r,r'\),\(B_r\) 和 \(B_{r'}\) 的串的起点集合不交。

    大体是反证考虑相交的部分,\(\lambda\) 应不同则 \(<_0,<_1\) 同时出现,则会有长度为 \(1\) 的周期,利用开头的空位和周期性可以导出一系列相等关系,破坏的 \(\text{Lyndon Word}\) 的成立,导致矛盾。

\(\text{Lyndon Tree}\) 的定义和性质:

  • 一个 \(\text{Lyndon}\) 串 \(w(|w|\geq 2)\) 的标准划分是有序对 \((u,v)\),满足 \(w=uv\) 且 \(v\) 是最小的严格后缀。
  • 定义:\(\text{Lyndon Tree}\) 是一棵树,每个节点都是一个 \(\text{Lyndon Word}\),根节点为 \(w\) 且 \(w\) 也是 \(\text{Lyndon}\) 串。节点的左右儿子分别为标准划分中的 \((u,v)\),叶子节点长度为 \(1\) 。
  • \(\text{Lyndon Tree}\) 本质为 \(SA\) 中 \(rank\) 数组的笛卡尔树。
  • 若子串 \(w[l,r]\) 是 \(\text{Lyndon Word}\),记 \(\alpha\) 为 \(\text{Lyndon Tree}\) 上 \(l\) 到 \(r\) 位置节点的 \(LCA\),\(\alpha\) 对应子串为 \(w[l_{\alpha},r_{\alpha}]\),则 \(l=l_{\alpha}\leq r\leq r_{\alpha}\)。若 \(w[l,r]=l_l(l)\),则对应的节点一定是一个右儿子节点。
  • \(w\) 的任意一个 \(\text{run}\) 的所有 \(\text{Lyndon Root}\) 都会在 \(w\) 关于 \(<_0\) 或 \(<_1\) 的 \(\text{Lyndon Tree}\) 的右儿子节点出现,\(0/1\) 取决于满足 \(\hat{w}[r+1]<_l \hat{w}[r+1-p]\) 的 \(l\) 。

\(\text{Weak Periodicity Lemma}\) (WPL):

  • 若一个串 \(S\) 有 \(p,q\) 两个周期,且 \(p+q\leq |S|\),则 \(\gcd(p,q)\) 也是 \(S\) 的周期。
  • \(\text{Periodicity Lemma}\):满足 \(p+q-\gcd(p,q)\leq |S|\) 时 \(\gcd(p,q)\) 便是 \(S\) 的周期。

计算 Runs

由于两个周期为 \(p\) 的 \(\text{runs}\) 的交长度必然 \(<p\),因此同一区域的 \(\text{Lyndon Root}\) 与 \(\text{Runs}\) 存在对应关系,根据 \(\text{Runs}\) 的第二条性质,可以通过计算 \(l_l(i)\) 来找到所有 \(\text{Lyndon Root}\),每个 \(\text{Root}\) 向两侧拓展即可找到包含自己的 \(\text{run}\) 。

考虑从右往左对于字符串 \(w\) 的每个后缀维护 \(\text{Lyndon}\) 分解 \(f_1..f_m\),显然 \(w[i,n]\) 处分解的 \(f_1\) 即 \(l_l(i)\)。每次向左移动时将字符 \(c\) 作为独立的一个 \(\text{Lyndon Word}\) 加入到 \(f\) 序列开头,如果序列中存在相邻的 \(u,v\) 满足 \(u<v\),则将 \(u\) 和 \(v\) 合并为 \(uv\) 并不断检查重复此过程,最后得到的就是 \(w\) 的 \(\text{Lyndon}\) 分解。由 \(\text{Lyndon Word}\) 的第六条性质:\(u<f_1\iff uf_1..f_m<f_1..f_m\),实际上 \(u<v\) 只需要比较 \(SA\) 数组即可。

枚举 \(i\),设 \(l_l(i)=[l,r]\),求出最大的 \(l_1,l_2\) 满足 \(w[l,l+l_1-1]=w[r+1,r+l_1]\),\(w[l-l_2,l-1]=w[r-l_2+1,r]\),若 \(l_1+l_2\geq r-l+1\),那么就找到了一个 \(\text{run}\):\((l-l_2,r+l_1,r-l+1)\) 。这里本质是一些 \(LCP\) 问题。

由于每个 \(\text{runs}\) 的 “正序” \(<_l\) 会有不同,因此要先枚举 \(l\) 按照两类字典序分别进行以上过程。

通过前面结论可知该算法可以找出 \(w\) 的所有 \(\text{runs}\),用 \(SA\)-\(IS\) 算法加 \(O(n)-O(1)\) \(RMQ\) 可以做到严格线性,简易实现可以轻易做到 \(O(n\log n)\) 。

子串半周期查询(Two-Period Queries)

问题:需要一个数据结构支持快速查询 \(S\) 的一个子串是否有不超过长度一半的周期,如果有则求出最小周期。

定义 \(exrun(i,j)\) 为满足 \(i'\leq i,j'\geq j,p\leq (j-i+1)/2\) 的一个 \(\text{run}\) \((i',j',p)\) 。若 \(exrun(i,j)\) 存在则一定唯一,否则重叠的 \(exrun\) 可以通过 WLP 导出矛盾。

算法:根据定义,找出 \(exrun(i,j)\) 即可回答对于子串 \(w[i,j]\) 的询问。对 \(<_0,<_1\) 分别建出 \(\text{Lyndon Tree}\),令 \(a_0=lca_0([i,\lceil(i+j)/2\rceil]),a_1=lca_1([i,\lceil(i+j)/2\rceil])\),并判断它们的右儿子是否满足条件。

正确性:假设 \(exrun(i,j) = r = (i',j',p)\),那么由于 \(p ≤ (j−i+1)/2\),一定有 一个 \(\text{Lyndon root}\) \(λ = S[i_λ,j_λ]\) 包含 \(⌈(j − i + 1)/2⌉\) 这个位置。根据 \(\text{Lyndon Tree}\) 的性质,这个 \(\text{Lyndon root}\) 会在关于 \(<_l\) 的树中作为某个节点的右儿子出现。 这时我们有 \(a_l\) 的长度 \(>p\),且它同样包含 \(⌈(j − i + 1)/2⌉\) 这个位 置,因此 \(a_l\) 是 \(λ\) 的祖先。若它的右儿子 \(β = S[i_β,j_β] \neq λ\),则 \(β\) 也是 \(λ\) 的祖先。因为 \(λ\) 和 \(β\) 都是右儿子,可以得到 \(i ≤ i_β < i_λ\)。 若 \(j_β ≤ j\) 则 \(S[i_β,j_β]\) 有周期 \(p\),与它是 \(\text{Lyndon Word}\) 矛盾。若 \(j_β > j\) 可以发现 \(S[i_λ,j_β] <_ℓ S[i_β,j_β]\),同样与它是 \(\text{Lyndon Word}\) 矛盾。 上述矛盾表明我们的算法是正确的。

该问题似乎默认 \(S\) 为 \(\text{Lyndon Word}\) 。此处存疑,几份资料中仅 command_block 的博客提到了非 \(\text{Lyndon}\) 串的 \(\text{Lyndon Tree}\) 建法:\(\text{Lyndon}\) 分解后各自建树,组成的森林即对应数据结构。然而此方法似乎并不能解决此问题。

应用

给定长为 \(n\) 的字符串 \(S\),要求将其划分为 \(s_1s_2...s_k(k>1)\),满足每个字串 \(s_i\) 是不循环的,且 \(s_i\neq s_{i+1}\)。求划分方案数,\(|S|\leq 2\times 10^5\) 。

#429. 【集训队作业2018】串串划分

本原平方串:形如 \(w=ss\) 的串,满足 \(w\) 的最小周期为 \(\frac{|w|}2\),被称为 \(\text{primitive square}\) 。

  • 若 \(SS\) 是 \(TT\) 的前缀,且 \(2|S|>|T|\),则 \(|T|-|S|\) 是 \(S\) 的周期。
  • 若 \(u,v,w\) 满足 \(uu\) 是 \(vv\) 的前缀,\(vv\) 是 \(ww\) 的前缀,且 \(uu\) 是本原平方串,则 \(|u|+|v|\leq |w|\) 。
  • 不同位置处的本原平方串个数总和为 \(O(n\log n)\) 级别。
  • 本质不同本原平方串个数为 \(O(n)\) 级别。

(证明均可见于前文给出的链接)

注意到题目要求相邻串不同,考虑容斥,在 \(s_1=s_2\) 时,\(s_1+s_2\) 与 \(s_1s_2\) 两种划分都是不合法的,可以想办法抵消。于是设划分 \(s_1s_2...s_k\) 的系数为 \(f(s_1)f(s_2)\cdot...\cdot f(s_k)\),\(f(s)=(-1)^{C(s)+1}\),\(C(s)\) 表示 \(s\) 的最小循环节的出现次数。

设 \(f(i)\) 表示前 \(i\) 位的所有划分的权值和,有转移

\[f(i)=\sum_{j=0}^{i-1}(-1)^{C(S[j+1,i])+1}f(j) \]

对于 \(C(s)=1\) 的情况是平凡的,维护前缀和转移即可。而 \(C(s)>1\) 的串 \(s\) 必然存在于一个 \(\text{run}\) \(\text{r}=(l,r,p)\) 中,且 \(s\) 必然包含一个本原平方串作为后缀。可以发现位置 \(i\) 利用 \(\text{r}\) 可以转移到的位置是一个公差为 \(p\) 的等差数列,对于一个 \(\text{r}\),在 \(DP\) 的过程中同时维护 \(p\) 个等差数列各自的和即可。

注意到不同位置的本原平方串个数是 \(O(n\log n)\) 级别的,一个本原平方串可以使其右端点进行一次上面的等差数列转移,所以转移数量也是 \(O(n\log n)\) 级别的。

于是可以先字符串哈希 \(O(n\log n)\) 求出所有 \(\text{runs}\),然后 \(O(n\log n)\) 计算 \(DP\) 即可。

#include<iostream>
#include<stack>
#include<algorithm>
#include<vector>
#define rep(i,a,b) for(int i = (a); i <= (b); i++)
#define per(i,b,a) for(int i = (b); i >= (a); i--)
#define N 200022
#define ll long long 
#define mod 998244353
#define Base 29
#define PII pair<int, int>
#define fr first
#define sc second
using namespace std;

struct StrHash{
    ll p[N], pow[N];
    void build(string s){
        int n = s.size();
        pow[0] = 1;
        rep(i,1,n){
            pow[i] = pow[i-1] * Base % mod;
            p[i] = (p[i-1] * Base + s[i-1] - 'a') % mod;
        }
    }
    bool equal(int l1, int r1, int l2, int r2){
        return (p[r1] + mod - p[l1-1] * pow[r1-l1+1] % mod) % mod == 
               (p[r2] + mod - p[l2-1] * pow[r2-l2+1] % mod) % mod;
    }
} Hash;

struct Run{ int l, r, p; } runs[N*2];

string s;
int n, tot, l[2][N];
ll f[N];
vector<ll> sum[N*2][2];
vector<int> ex[N];

int extend(int i, int j, int dir){
    if(s[i-1] != s[j-1]) return 0;
    int l = 1, r = (dir ? n-max(i,j)+1 : min(i, j)), mid;
    while(l < r){
        mid = (l+r+1)>>1;
        if(dir ? Hash.equal(i, i+mid-1, j, j+mid-1) : Hash.equal(i-mid+1, i, j-mid+1, j)) l = mid;
        else r = mid-1;
    }
    return l;
}
int cmp(int l1, int r1, int l2, int r2){
    int len = extend(l1, l2, 1), len1 = r1-l1+1, len2 = r2-l2+1;
    if(len == min(len1, len2)) return len1 < len2 ? -1 : (len1 == len2 ? 0 : 1);
    else return s[l1+len-1] < s[l2+len-1] ? -1 : (s[l1+len-1] == s[l2+len-1] ? 0 : 1);
}

void lyndon(int id){
    stack<int> stk;
    per(i,n,1){
        while(!stk.empty() && cmp(i, n, stk.top(), n) == (id ? 1 : -1)) stk.pop();
        l[id][i] = stk.empty() ? n : stk.top()-1;
        stk.push(i);
    }
}

void check(int l, int r){
    int l1 = extend(l, r+1, 1), l2 = extend(l-1, r, 0);
    if(l1+l2 >= r-l+1) runs[++tot] = {l-l2, r+l1, r-l+1};
}

int main(){
    ios::sync_with_stdio(false);
    cin>>s, n = s.size();

    Hash.build(s);
    rep(id,0,1){
        lyndon(id);
        rep(i,1,n) check(i, l[id][i]);
    }
    sort(runs+1, runs+tot+1, [&](Run a, Run b){ 
        return a.l != b.l ? a.l < b.l : (a.r != b.r ? a.r < b.r : a.p < b.p); });
    tot = unique(runs+1, runs+tot+1, [&](Run a, Run b){ return a.l == b.l && a.r == b.r; }) - runs-1;

    rep(i,1,tot){
        Run r = runs[i];
        rep(j,r.l+2*r.p-1,r.r) ex[j].push_back(i);
        int siz = r.p;
        if(r.l + 3*r.p-1 > r.r) siz = (r.r-r.l+1) % r.p + 1;
        rep(_,0,1) sum[i][_].resize(siz);
    }

    f[0] = 1; ll pref = 1;
    rep(i,1,n){
        f[i] = pref;
        for(int k : ex[i]){
            Run r = runs[k];
            int id = (i - r.l + 1) % r.p;
            (sum[k][0][id] += f[i-2*r.p]) %= mod;
            (sum[k][1][id] *= mod-1) %= mod, (sum[k][1][id] += mod - f[i-2*r.p]) %= mod;
            (f[i] += sum[k][1][id] + mod - sum[k][0][id]) %= mod;
        }
        (pref += f[i]) %= mod;
    }
    cout<< f[n] <<endl;
    return 0;
}

标签:Runs,Word,int,text,Tree,runs,Lyndon,mod
来源: https://www.cnblogs.com/Neal-lee/p/16353936.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有