zoukankan      html  css  js  c++  java
  • Lyndon分解和最小循环表示学习

    做CF594E涉及的两个知识点。以下字符串采用Python记法。

    Lyndon分解

    定义 $S$ 是Lyndon串,当且仅当对于任意有意义的正整数 $i$ 有 $S<S[i:]$.

    定义 $S$ 的Lyndon分解是一个Lyndon串的序列 $s_1, s_2, ldots, s_n$, 使得 $S=s_1s_2 cdots s_n$ 并且 $s_1 ge s_2 ge cdots ge s_n$.

    Lyndon分解存在且唯一。

    不难发现,Lyndon分解可以这么得到:对于 $S$, 取最小的后缀 $S[i:]$, $S$ 的Lyndon分解,是在 $S[:i]$ 的Lyndon分解的最后加上一项 $S[i:]$ 得到的序列。

    于是,Lyndon分解可以用后缀数组求,但是这样太复杂了。

    Lyndon分解的Duval算法:

    设原字符串为 $S$. 逐个加入字符,设已能确定 $S[:i]$ 的Lyndon分解 $s_1, s_2, ldots, s_n$ 为 $S$ 的Lyndon分解的前缀,再尽可能地扩充字符串 $S[i:k]$, 使得 $S[i:k]$ 具有Lyndon周期 $t$, 也就是说,$S[i:k]=(m imes t)u$, 其中 $m$ 是正整数,$t$ 是Lyndon串,$u$ 是 $t$ 的(可以为空的)真前缀。考虑加入字符 $S[k]$, 若 $k=|S|$ 则令 $S[k]=-infty$.

    • 若 $S[k]<S[k-|t|]$, 则 $S[i:k+1]$ 就没有Lyndon周期了。不过此时 $S[:i+m|t|]$ 的Lyndon分解已确定,即在 $S[:i]$ 的Lyndon分解末尾添加 $m$ 个 $t$, 取新参数 $i gets i+n|t|$ 重来。
    • 若 $S[k]=S[k-|t|]$, 则 $t$ 也是 $S[i:k+1]$ 的Lyndon周期。
    • 若 $S[k]>S[k-|t|]$, 则 $S[i:k+1]$ 的Lyndon周期是其本身。

    实现上我们只需要维护 $i, k$ 以及 $j=k-|t|$. 代码链接

    扩充 $S[i:k]$ 的过程中 $i+kle2|S|$ 且随扩充总轮数递增,因此该算法时间 $O(|S|)$, 除去输入输出只需要 $O(1)$ 额外空间,是一个非常简短而高效的算法。

    最小循环表示

    最小循环表示,也就是对于字符串 $S$, 求出最小的 $T_i=S[i:]S[:i]$.

    我们维护两个决策 $i, j$, 满足 $i<j$ 并且 $[0, i)$ 和 $(i, j)$ 中的整数都不是最优决策。

    初始时,设 $i=0, j=1$.

    我们通过枚举比较求出 $T_i$ 和 $T_j$ 的最长公共前缀 $k$.

    • 若 $T_i=T_j$, $T_i$ 即为最优解。这是因为 $S$ 以 $j-i$ 为循环节循环,而 $(i, j)$ 中任何整数都不是最优决策。
    • 若 $T_i<T_j$, 说明 $[j, j+k]$ 内的整数不是最优决策,因为它总比 $[i, i+k]$ 内的对应决策劣,因此令 $j gets j+k+1$. 上述性质未改变。
    • 若 $T_i>T_j$, 同理令 $i gets i+k+1$, 若此后 $i ge j$ 则再令 $j gets i+1$, 上述性质仍未改变。

    当 $j ge |S|$ 时,我们发现 $[0, i)$ 和 $(i, |S|)$ 中的整数都不可能为最小决策了,所以 $T_i$ 是唯一的最优解;否则重复此过程。

    $i+j+k le 3|S|$ 随枚举总次数递增,因此该算法时间 $O(|S|)$, 除去输入输出只需要 $O(1)$ 额外空间,是一个非常简短而高效的算法。

    代码:$n$($n le 3 imes10^5$)项整数序列的最小循环表示。

    不过这份代码由于在OJ上刷常数榜,不仅加入了输入输出优化,还破环为链以节省取模的时间,空间略大。

     1 #include <bits/stdc++.h>
     2 char r[1<<25], *rc=r, w[1<<25], *wc=w;
     3 int rd() {
     4     int a;
     5     bool b;
     6     unsigned char c;
     7     while(c=*rc++-48, c>9&c!=253);
     8     b=(c==253);
     9     for(a=b?0:c; (c=*rc++-48)<10; a=a*10+c);
    10     return b?-a:a;
    11 }
    12 void wr(int v) {
    13     char s[10], *p=s;
    14     while(*p++=v%10+48, v/=10);
    15     while(*wc++=*--p, p!=s);
    16 }
    17 const int N=3e5;
    18 int n, x[2*N];
    19 int main() {
    20     int i, j, k;
    21     fread(r, 1, 1<<25, stdin);
    22     n=rd();
    23     for(i=0; i<n; ++i) x[i]=rd();
    24     memcpy(x+n, x, n*sizeof(int));
    25     for(i=0, j=1, k=0; j<n&&k<n; ) {
    26         int xi=x[i+k], xj=x[j+k];
    27         if(xi==xj) ++k;
    28         else if(xi<xj) j+=k+1, k=0;
    29         else {
    30             i+=k+1, k=0;
    31             if(i>=j) j=i+1;
    32         }
    33     }
    34     for(k=0; k<n; ++k) wr(x[i+k]), *wc++=' ';
    35     wc[-1]='
    ';
    36     fwrite(w, 1, wc-w, stdout);
    37     return 0;
    38 }
    最小循环表示
  • 相关阅读:
    用mescroll实现无限上拉增加数据,下拉刷新数据 (学习笔记)
    jackson使用问题:mapper.readValue()将JSON字符串转反序列化为对象失败或异常
    常用 NHibernate.Criterion
    Threading
    并口
    电子称 弹钱箱脉冲
    ZIP文件解压
    wpf 异步加载 只需6段代码
    Newtonsoft.Json使用
    接口post +json +bean
  • 原文地址:https://www.cnblogs.com/nealchen/p/12002895.html
Copyright © 2011-2022 走看看