【模板】KMP算法、fail树

ACM模板


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,,sp},满足以上条件,称 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 sk 是 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;
}

猜你喜欢

转载自blog.csdn.net/Fighting_Peter/article/details/112724803