ACM模板
目录
- KMP字符串
- Fail失配树
KMP字符串
肖然大佬视频讲解
子串: 从原串中选取连续的一段,即为子串(包括空串)
前缀: pre(s,k)pre(s,k)pre(s,k) 为 s 前 k 个字符构成的子串
后缀: suf(s,k)suf(s,k)suf(s,k) 为 s(k…n) 构成的子串
任何子串都是某个后缀的前缀
最长公共前缀 lcp(s,t)lcp(s,t)lcp(s,t) 和最长公共后缀 lcs(s,t)lcs(s,t)lcs(s,t)
周期: ①0<p<∣s∣0<p<|s|0<p<∣s∣ ②s[i]=s[i+p],∀i∈{1,2,…,∣s∣−p}s[i]=s[i+p], \forall i\in\{1,2,\dots,|s|-p\}s[i]=s[i+p],∀i∈{1,2,…,∣s∣−p},满足以上条件,称 ppp为 s 的周期
Border: ①0<r<∣s∣0<r<|s|0<r<∣s∣ ②pre(s,r)=suf(s,r)pre(s,r)=suf(s,r)pre(s,r)=suf(s,r),满足以上条件,称 pre(s,r)pre(s,r)pre(s,r)为 s 的 border
周期与Border的关系:pre(s,k)pre(s,k)pre(s,k)是 s 的 border ⇔\Leftrightarrow⇔ ∣s∣−k|s|-k∣s∣−k 是 s 的周期
Border的传递性:
①串 s 是 t 的 border ,串 t 是 r 的 border,那么 s 是 r 的border
②串 s 是 r 的 border,串 t (∣t∣>∣s∣|t|>|s|∣t∣>∣s∣)也是 r 的 border,则 s 是 t 的border
记 mb(s) 表示 s 的最长 border 则 mb(s),mb(mb(s))…构成 s 的所有 border
给定一个模式串S,以及一个模板串P,所有字符串中只包含大小写英文字母以及阿拉伯数字。
模板串P在模式串S中多次作为子串出现。
求出模板串P在模式串S中所有出现的位置的起始下标(0开始)。
#include<iostream>
using namespace std;
const int N=1000010;
int n,m;
char p[N],s[N];
int ne[N];
int main()
{cin>>n>>p+1>>m>>s+1;// 求ne过程看成两个相同的串匹配for(int i=2,j=0;i<=n;i++){while(j&&p[i]!=p[j+1]) j=ne[j];if(p[i]==p[j+1]) j++;// i结尾能够匹配 1~j 那么ne[i]=jne[i]=j;}// 当前需要判断是否匹配 p[j+1]?=s[i]for(int i=1,j=0;i<=m;i++){while(j&&s[i]!=p[j+1]) j=ne[j];if(s[i]==p[j+1]) j++;if(j==n){cout<<i-n<<' ';j=ne[j];}}return 0;
}
Fail失配树
概念以及构造: 将 next[i] 视为 i 点的父节点,那么通过 next 数组可以把 0~N 点连成一棵树,满足性质:
- 点 i 的所有祖先都是前缀 pre(s,i) 的 border
- 没有祖先关系的两个点 i,j 没有 border 关系
联系: 计算 next[i] 的过程可以看作:从 j=fa[i-1] 开始不断往上走,找到第一个满足 s[j+1]=s[i] 的点,把点 i 的父亲设为 j+1
#include<string>
#include<iostream>
using namespace std;
const int N=1000010;
int ne[N];
int fa[N][21],dep[N];
string s;
int n;
int lca(int a,int b)
{if(dep[a]<dep[b]) swap(a,b);for(int k=20;k>=0;k--)if(dep[fa[a][k]]>=dep[b]) a=fa[a][k];if(a==b) return a;for(int k=20;k>=0;k--)if(fa[a][k]!=fa[b][k]){a=fa[a][k];b=fa[b][k];}return fa[a][0];
}
int main()
{cin>>s;n=s.size();s="."+s;dep[0]=1,dep[1]=2;fa[1][0]=0;for(int i=2,j=0;i<=n;i++){while(j&&s[i]!=s[j+1]) j=ne[j];if(s[i]==s[j+1]) j++;ne[i]=j;// 构建失配树fa[i][0]=j;dep[i]=dep[j]+1;for(int k=1;k<=20;k++)fa[i][k]=fa[fa[i][k-1]][k-1];}int q;cin>>q;while(q--){int a,b;cin>>a>>b;int pab=lca(a,b);// 注意本身是公共祖先的情况if(a==pab||b==pab) cout<<ne[pab]<<'\n';else cout<<pab<<'\n';}return 0;
}