Border 理论

定义

\(S'\) 同时为 \(S\) 的前缀和后缀,且 \(S'\ne [\mathbb\emptyset],S'\ne S\),则 \(S'\)\(S\)\(\text{Border}\)\(\text{Border}\) 为一个字符串,但有时也指代其长度)

\(\mathcal B(S)\)\(S\)\(\text{Border}\) 集合

对于 \(k\in\mathbb N,k<|S|\),若 \(\forall i>k,S_i=S_{i-k}\),则 \(k\)\(S\)周期\(\operatorname{Period}\))(周期为正整数)。若 \(k\)\(|S|\) 的因数,则 \(k\)\(S\)整周期

\(\mathcal P(S)\)\(S\) 的周期集合

定理 1\(t\)\(S\) 最长 \(\text{Border}\),则 \(\mathcal B(S)=\mathcal B(t)\cup\{t\}\)

定理 2\(t\in\mathcal B(S)\iff |S|-|t|\in \mathcal P(S)\)

前缀数组

定义

对于 \(S\),令 \(n=|S|\),定义其前缀数组 \(\pi_i\)\(\operatorname{prf}(S,i)\) 的最长 \(\text{Border}\) 长度

也称为前缀函数

模板

// 前缀函数
template <typename value_t, typename output_iter_t, ass_is_RAI(output_iter_t)>
    void prefix_function(const basic_string<value_t> &s, output_iter_t pi){//[0,pi_i)=[i-pi_i,i)
        if (s.empty())return;
        size_t n = s.size();
        *pi = 0;
        for (size_t i = 1, j = 0; i != n; ++i){
            while (j && s[i] != s[j])j = pi[j - 1];
            if (s[i] == s[j])++j;
            pi[i] = j;
        }
    }

时间复杂度 \(O(n)\)

KMP 算法

全称为 \(\text{Knuth–Morris–Pratt}\) 算法,是一种单模式串字符串匹配算法

模板:

// KMP 算法
template <typename value_t, typename output_iter_t, ass_is_RAI(output_iter_t)>
    vector<size_t> kmp(const basic_string<value_t> &text, const basic_string<value_t> &pat, output_iter_t pi){//返回匹配位置
        if (text.size() < pat.size())return {};
        if (text.size() == pat.size()){if (text == pat)return {0};return {};}
        if (pat.empty())return {};
        prefix_function(pat, pi);
        size_t n = text.size(), m = pat.size();
        vector<size_t> ret;
        for (size_t i = 0, j = 0; i != n; ++i){
            while (j && text[i] != pat[j])j = pi[j - 1];
            if (text[i] == pat[j])++j;//[i-m+1,i]=[0,j)
            if (j == m)ret.emplace_back(i - m + 1), j = pi[j];
        }
        return ret;
    }

时间复杂度 \(O(n+m)\)

失配树

建立 \(n+1\) 个点,编号 \(0\sim n\),依次表示长为 \(n\) 的字符串 \(S\) 的每个前缀

对于 \(1\le i\le n\)\(i\)\(\pi_i\) 连有向边,则构成一棵以 \(0\) 为根的内向树,称为失配树

对于 \(S\) 的每个前缀,其 \(\text{Border}\) 集合为它在树上所有非根祖先(不含自身)

例:CF1286E Fedya the Potter Strikes Back

给定 \(s_{1\sim n}\)\(w_{1\sim n}\),对于每个 \(1\le i\le n\),求出 \(\sum_{s[L:R]=s[1,R-L+1],R\le i}\min_{j=L}^R w_j\),强制在线(即每次只给出一组 \((s_i,w_i)\)),\(n\le6\times10^5\)

显然长度为 \(i\) 时的答案等于长度 \(i-1\) 时的答案 加上 \(\sum_{s[L:i]=s[1,i-L+1]}\min_{i=L}^i w_i\),考虑计算之

这等于 \(\sum_{s'\in\mathcal B(\operatorname{prf}(s,i))}\min_{k=i-|s'|+1}^i w_k\)

\(\min_{k=a}^i w_k=Sw(a)\),使用单调栈 或 \(\operatorname{ST}\) 表 维护之,做到 \(O(\log n)\)(单调栈上二分)或 \(O(1)\)\(\operatorname{ST}\) 表)查询

转化为求解 \(\sum_{s'\in\mathcal B(\operatorname{prf}(s,i))}Sw(i-|s'|+1)\)

\(Bp(S,i)=\{i-|s'|+1\mid s'\in \mathcal B(S)\}\)

则转化为求 \(\sum_{l\in Bp(\operatorname{prf}(s,i))}Sw(l)\)

考虑维护集合 \(R^\ast =\{Sw(l)\mid l\in Bp(\operatorname{prf}(s,i))\}\)

先考虑从 \(Bp(\operatorname{prf}(s,i-1))\)\(Bp(\operatorname{prf}(s,i))\) 的变化

\(s_i=s_1\),则 \(Bp(\operatorname{prf}(s,i))\) 会在 \(Bp(\operatorname{prf}(s,i-1))\) 的基础上加入 \(i\)(显然加入 \(R^\ast\) 的最多只有这一个)

然后从中删去不再合法的部分,即集合 \(Bp(\operatorname{prf}(s,i-1))-Bp(\operatorname{prf}(s,i))\)

\(l\in (Bp(\operatorname{prf}(s,i-1))-Bp(\operatorname{prf}(s,i)))\) 等价于 \(s_i\ne s_{i-l+1}\),因此问题转化为快速求出所有满足 \(s_i\ne s_{i-l+1}\)\(l\)

维护数组 \(fadf_i\),表示最大的 \(l\) 使得 \(s_{l+1}\ne s_{i+1}\),且 \(l\in\mathcal B(i)\)

显然得到 \(s_i\) 后可以 \(O(1)\) 求出 \(fadf_{i-1}\),且 \(fadf_1\sim fadf_{i-2}\) 不再改变

\(pi_i\)\(s\) 的前缀数组

\(j\)\(i-1\) 开始,若 \(s_{j+1}\ne s_i\)\(l=j+1\) 为满足 \(s_i\ne s_{i-l+1}\)\(l\),此时令 \(j\) 跳到 \(fadf_j\);否则令 \(j\) 跳到 \(pi_j\)

这样可以均摊 \(O(1)\) 找到所有满足要求的 \(l\)

此时已经可以在可接受复杂度内维护 \(Bp(\operatorname{prf}(s,i))\) 了,考虑如何维护 \(\sum_{l\in Bp(\operatorname{prf}(s,i))}Sw(l)\),这等价于维护 \(R^\ast\) 的元素和

对于集合 \(Bp(\operatorname{prf}(s,i))\),操作只有加入和删除两种,因此 \(R^\ast\) 需要支持插入、删除、对给定数取 \(\min\),查询总和,可以用 std::map 均摊 \(O(\log n)\) 维护上述操作

总时间复杂度 \(O(n\log n)\)

代码

弱周期引理 与 周期引理

弱周期引理

\[(\forall p,q\in \mathcal P(S),p+q\le |S|),\gcd(p,q)\in\mathcal P(S) \]

定理 3:若 \(t\)\(s\) 的前缀,且 \(a\in \mathcal P(s), b\in\mathcal P(t), b\mid a, |t|\ge a\),且 \(b\)\(t\) 的整周期,则有 \(b \in \mathcal{P}(s)\)

周期引理

\[(\forall p,q\in \mathcal P(S),p+q-\gcd(p,q)\le |S|),\gcd(p,q)\in\mathcal P(S) \]

其他常用定理

定理 4:若 \(s\)\(t\) 中匹配了多次,且 \(|t|\ge \frac{|s|}2\),则每次匹配的位置形成一个公差为 \(s\) 的最小周期长度的等差数列

例:CF1038F Wrap Around

给定 \(0/1\) 字符串 \(s\),求有多少长为 \(n\) 的字符串 \(t\),满足 \(t^{\infty}\)(表示以 \(t\) 为循环节的无限循环字符串)包含 \(s\)\(|s|\le n\le40\),加强到 \(4000\)

使用定理 4 的方法

由于 \(|s|\le n\),显然 \(s\)\(t\) 的子串,或 \(s\) 的前缀为 \(t\) 的后缀且 \(s\) 剩余部分为 \(t\) 的前缀

对于某个 \(t\),若令 \(f_i\) 表示 \(t\)\(i\) 开始长为 \(|s|\) 的子串(超过 \(m\) 则从头开始)是否与 \(s\) 相同,则 \(t\) 合法等价于 \(f\) 中至少有一个 \(1\)

考虑容斥,令 \(p_x=\sum_{|S|=x}|\{f|\forall i\in S,f_i=1\}|\),则答案为 \(\sum_{i=1}^n(-1)^{i+1}p_i\)

若一个 \(t\) 合法,则其所有循环同构串都合法,因此只统计 \(f_1=1\)\(t\) 以避免重复

一个 \(f_1=1\)\(t\) 对应 \(n-y+1\) 个合法的 \(t\),其中 \(y\) 为最后一个 \(f_i=1\)\(i\)

\(dp_{i,j,k}\) 为当前填到 \(t_i\)\(1\sim i\)\(f_i\) 最后一个为 \(1\) 的位置为 \(j\)\(f_i\)\(1\) 的数量至少为 \(k\)

\(g_i\) 表示 \(s\) 是否有长为 \(m-i\)\(\text{Border}\)

若令 \(f_i=1\),则 \(dp_{i-1,x,k-1}\rightarrow dp_{i,i,k}\)。此时若有 \(x+m<i\),则要求 \(s\) 有长 \(m-(i-x)\)\(\text{border}\),否则会与之前填的矛盾;若有 \(i+m-1>n\),则要求 \(s\) 有长为 \(m-(n-i+1)\)\(\text{border}\),否则会和前缀的 \(f\) 矛盾

若令 \(f_i=0\),则有两种情况:

  • \(i<x+m\),则当前位置的 \(t\) 被最后一个 \(f\) 限制,只有一种取值,\(f_{i-1,x,k}\rightarrow f_{i,x,k}\)
  • 否则可以填 \(0/1\)(由于 \(dp\) 值的定义为至少,因此这里不用考虑新产生 \(f_i=1\) 的情况),\(2f_{i-1,x,k}\rightarrow f_{i,x,k}\)

时间复杂度 \(O(n^3)\)

发现最后一维 \(k\) 在容斥中只需要其奇偶性,因此 \(k\) 可以只取 \(0/1\),时间复杂度降为 \(O(n^2)\)\(dp\) 第一维可以滚动数组优化,空间复杂度 \(O(n)\)

代码


定理 5:字符串 \(s\) 所有长度不小于 \(\frac{|s|}2\)\(\text{Border}\) 长度构成等差数列

定理 6:字符串所有 \(\text{Border}\) 长度排序后可以划分成 \(\lceil\log_2|s|\rceil\) 个连续段,使得每段都为等差数列

例:P4156 [WC2016] 论战捆竹竿

给定长为 \(n\) 的字符串 \(s\),定义拼接为将一个 \(s\) 的后缀(可以为全部)接在另一个 \(s\) 之后,满足前者剩余的前缀为后者的后缀,求将若干 \(s\) 拼接后可以到达的不超过 \(w\)长度数量,\(w\le10^{18},n\le5\times10^5\),多测 \(T\le5\)

\(a_{1\sim ct}\)\(\mathcal P(s)\) 从小到大排序的结果,则题目等价于求 \([0,w-n]\) 中可以表示为 \(\sum_{i=1}^{ct} a_i x_i\) 的数量,其中 \(x_i\in\mathbb N\)

按一般的同余最短路,定义 \(f_i\;(0\le i< a_1)\) 为走到任意 \(\bmod a_1=i\) 的位置经过的最小长度,则答案为

\[\sum_{i=0}^{a_1-1}\left\lfloor\frac{w-f_i}{a_1}\right\rfloor+1 \]

按这样直接计算为 \(O(n\cdot ct\cdot \log n)=O(n^2\log n)\)

\((F,D,C)\) 表示首项为 \(F\),公差为 \(D\),末项为 \(F+DC\) 的等差数列

\(a\) 可以划分为 \(O(\log n)\) 个连续等差子段

则原题可以转化为两个子问题:

  1. \(O(n)\) 内用一个等差数列 \((F,D,C)\) 跟新 \(f\)\(f\) 的模数为 \(F\)
  2. \(O(n)\) 内转换 \(f\) 的模数

对于第一个问题,可以列出转移方程(其中 \(f'\) 为旧 \(f\) 数组):

\[f_i=\sum_{j=0}^C f'_{(i-F-jD)\bmod F}+jD=\sum_{j=0}^C f'_{(i-jD)\bmod F}+jD \]

直接计算时间复杂度为 \(O(F\cdot C)\)

发现 \([0,F)\) 内的不同模 \(\gcd(F,D)\) 同余类之间不存在转移,因此分别考虑,以下只考虑其中一个同余类

\(p\) 为当前同余类中 \(f'\) 最小值的下标

显然在上述转移过程中,\(f_p=f'_p\),且不存在跨越 \(p\) 的最优跟新(跨越 \(p\) 定义为 \(j<p<i\),或 \(p<i<j\),或 \(i<j<p\))(\(j\) 跟新 \(i\) 定义为 \(j\) 使 \(f_i\) 的值严格变小)(若存在跨越的,则令 \(j=p\),一定不劣于跨越的 \(j\)

因此从 \(p\) 开始处理这个同余类

此时转移方程变为滑动窗口的形式,单调队列解决即可

对于第二个问题,设 \(f'\) 为模数跟新为 \(NewMd\) 后的 \(f\) 数组,\(f\) 为未跟新时的数组,\(Nwmd\) 为跟新前模数,则

\[f_i+k\cdot Nwmd\rightarrow f'_{(f_i+k\cdot Nwmd)\bmod NewMd} \]

同样各个模 \(\gcd(Nwmd,NewMd)\) 同余类分开处理

形式类似第一个问题的 \(dp\),但滑动窗口变为前缀,直接计算即可

时间复杂度 \(O(\sum n\log n)\),常数极大

代码(其中的 \(hash\) 判断是为了\(\text{UOJ}\)\(\text {Extra Test}\)


定理 7:回文串的回文前 / 后缀即为其 \(\text{Border}\)

由此可推出一个字符串所有 \(\text{Border}\) 按长度从大到小排序,其中回文的恰好为一个可空后缀,即 \(fail\) 树上回文的串为包含根的连通块或不存在

定理 8:若回文串 \(S\) 有周期 \(p\),则可以把 \(\operatorname{prf}(S,p)\) 划分成长度为 \(|S|\bmod p\) 的前缀和长度为 \(p-|S|\bmod p\) 的后缀,使得它们都是回文串

定理 9:若 \(B\) 是回文串 \(S\) 的最长 \(\text{Border}\)\(|B| \ge \frac{|S|}{2}\),则 \(B\)\(S\) 中恰能匹配 \(2\)

重要后缀(Significant Suffixes)

\(\operatorname{Ssf}(S)\) 为在 \(S\) 后接一个字符串(可以为空)后可能成为 \(S\) 的最小后缀的后缀集合

定理 10:对于任意字符串 \(S\)\(u, v\in \text{Ssf}(S),|u|<|v|\),有 \(u\)\(v\)\(\text{Border}\)

定理 11:对于任意字符串 \(S\)\(u, v\in\text{Ssf}(S),|u|<|v|\),有 \(2|u|\le|v|\)

定理 12:\(|\text{Ssf}(s)| \le \log_2|s|\)

例:P5211 [ZJOI2017] 字符串

长为 \(n\) 的序列,\(q\) 次操作,区间加减或区间查询字典序最小后缀起始位置(仅保留该区间的情况下),\(n\le2\times10^5,q\le3\times10^4\)

线段树维护序列,每个节点保存对应区间内的 \(\text{Ssf}\) 集合

可证父区间的 \(\text{Ssf}\) 一定为两子区间的 \(\text{Ssf}\) 的并的子集

因此合并区间时,暴力枚举儿子的每个后缀,判断是否是父亲的 \(\text{Ssf}\) 即可

需要使用分块哈希的方式维护原串,做到单次 \(O(1)\) 比较后缀

时间复杂度 \(O(n\log^2 n+m\log^3 n+m\sqrt n)\)

代码

参考

  1. Border 理论小记
posted @ 2025-03-15 16:28  Hstry  阅读(9)  评论(0)    收藏  举报