• 最小表示法


    参考资料

    约定:

    字符串的下标从 \(0\) 开始。\(|s|\) 表示字符串 \(s\) 的长度。
    对于字符串 \(s\),记其每一个字符分别为 \(s_0, s_1, \cdots, s_{|s|-1}\)
    子串 \(s_l, s_{l+1}, \cdots, s_{r-1}, s_r\) 简记为 \(s[l:r]\)。特别地,若 \(l=0\),可记作 \(s[:r]\);若 \(r=|s|-1\),可记作 \(s[l:]\)
    对于字符串 \(a, b\)\(a+b\) 表示拼接操作,即将字符串 \(b\) 拼接到字符串 \(a\) 之后,构成新的字符串。
    记构成的新字符串为 \(c\),则上述拼接操作记为 \(c\gets a+b\)
    其中符号 \(x\gets y\) 表示将 \(y\) 的值赋给 \(x\)
    不论是字符还是字符串,皆不加引号。
    本文中的加法均在模意义下进行。

    模板题
    \(\exist i,\ 1\leqslant i\leqslant |s|-1,\ t=s[i:]+s[:i-1]\),称 \(s\)\(t\) 互为循环同构串。
    我们要求的是字符串 \(s\) 的所有循环同构串中字典序最小的一个,并称其为最小表示。
    方便起见,记 \(s\left<i\right>=s[i:]+s[:i-1]\) ,即以 \(i\) 为开始的循环同构串。

    暴力比较显然不行,我们要利用循环同构串的性质。
    比如我们在比较 \(s\left<i\right>\)\(s\left<j\right>\),在第 \(k+1\) 位首次出现不同,不妨设 \(s_{i+k}>s_{j+k}\)
    此时,\(\forall l,\ 0\leqslant l \leqslant k\),均有 \(s\left<i+l\right>\) 不是最小表示,因为一定有 \(s\left<j+l\right>\) 比其更优。
    因此,出现这种情况时,直接从 \(s\left<i+k+1\right>\) 开始比较即可。这样我们就完成了优化。

    code:

    int i=0,j=1,k=0;
    while(i<n&&j<n&&k<n)
    {
        if(s[(i+k)%n]==s[(j+k)%n]){k++;continue;}//相等则比较下一位
        if(s[(i+k)%n]<s[(j+k)%n])j=j+k+1;//利用性质进行优化
        else i=i+k+1;
        if(i==j)j++;//我们要保证i和j始终不相等
        k=0;//更换了i和j,我们要从头进行比较
    }
    ans=min(i,j);
    

    时间复杂度为 \(O(n)\)。因为 \(i,\ j\) 始终在增加(\(k\) 增加也相当于是 \(i\)\(j\) 在增加了)

  • 相关阅读:
    剑指offer系列0:替换空格&从头到尾打印链表
    算法1:动态规划
    设计模式2:策略模式
    NPOI导出xls、xlsx和csv
    EF6
    oracle导出数据字典
    oracle分组函数
    oracle分析函数中的开窗函数
    Oracle 列转行&行转列
    Oracle基本函数总结
  • 原文地址:https://www.cnblogs.com/pjykk/p/15856533.html
Copyright © 2020-2023  润新知