KMP字符串
子串: 从原串中选取连续的一段,即为子串(包括空串)
前缀: p r e ( s , k ) pre(s,k) pre(s,k) 为 s 前 k 个字符构成的子串
后缀: s u f ( s , k ) suf(s,k) suf(s,k) 为 s(k…n) 构成的子串
任何子串都是某个后缀的前缀
最长公共前缀 l c p ( s , t ) lcp(s,t) lcp(s,t) 和最长公共后缀 l c s ( 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},满足以上条件,称 p p p为 s 的周期
Border: ① 0 < r < ∣ s ∣ 0<r<|s| 0<r<∣s∣ ② p r e ( s , r ) = s u f ( s , r ) pre(s,r)=suf(s,r) pre(s,r)=suf(s,r),满足以上条件,称 p r e ( s , r ) pre(s,r) pre(s,r)为 s 的 border
周期与Border的关系: p r e ( 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]=j
ne[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;
}