KMP

应用范围

对于一个匹配串(S)和单个或多个母串(B)的问题

流程

  • 先对匹配串造fail数组和AC自动机中的fail类似,即以失败的节点为右端点的

这里写图片描述

  • 复杂度约为 On+m
void pre(){
    fail[1]=0;fail[2]=1;
    for(int i=1;i<=m;i++){
        int j=fail[i];
        while(j&&S[i]!=S[j])j=fali[j];
        //此时满足S[i-j+1,i]和S[1,j]相等 
        fail[i+1]=j+1;
    }
}
  • 和母串进行匹配
void match(){
    for(int i=1,j=1;i<=n;i++){
        while(j&&B[i]!=S[j])j=fali[j];
        //此时满足B[i-j+1,i]和S[1,j]相等 
        j++;
        if(j=m+1){
            //get the ans
            j=fail[j];
        }
    }
} 

一些变形

  • 对于一些问题相等的判断肯定不会只是数值的相等,可能有一些奇葩的定义

例1

  • 我们定义两个字符串相等:是他们的其中的每个字符在该段中的相对大小一一对应,就像下面的例子(串中字符大小属于[1,26])
5 6 2 10 10 7 3 2 9
    1  4  4 3 2 1
  • 此时我们只需改变判断相等的方法即可,但是一定不能忽略了匹配的流程:一个一个的判断(即当前有一对相同的串,我们只需判断它们的尾端加入后新的串是否相等)
  • 我们发现字符大小很小,于是我们可以记录一个关于[1,26]前缀数组即可
bool cmp(int A[M][30],int l1,int r1,int a,int B[M][30],int l2,int r2,int b){
    for(int i=1;i<=t;i++){
        ta[i]=A[r1][i]-A[l1-1][i]+ta[i-1];
        tb[i]=B[r2][i]-B[l2-1][i]+tb[i-1];
    }
    return ta[a-1]==tb[b-1]&&ta[a]==tb[b];//加入前后都相等 
}
int main(){
    /**读入,构造前缀和**/ 
    fail[2]=1;
    for(int i=2,j;i<=m;i++){
        j=fail[i];
        while(j&&!cmp(Ss,1,j-1,S[j],Ss,i-j+1,i-1,S[i]))j=fail[j];
        fail[i+1]=j+1;
    }
    for(int i=1,j=1;i<=n;i++){
        while(j&&!cmp(Bs,i-j+1,i-1,B[i],Ss,1,j-1,S[j]))j=fail[j];
        j++;
        if(j==m+1)ans[++ans[0]]=i-m+1,j=fail[j];
    }
}

例2

  • 对于两端串,如果它们的“样子一样”,我们认为它们相等
a   b   c   x  c    z   z   a   b   c 
      prvi dr prvi  tr  tr  x   

-如图 prvi>c||dr>x||tr>z||x>a (唯一对应)
- 方法很简单,我们只需记录每个位置的字符上一次出现的下标,即可判断一个新的点(段尾后一个)在该段中的位置,用它判断即可

bool cmp1(int l1,int r1,int l2,int r2){
    int ra=nxts[r1];
    int rb=nxts[r2];
    if(ra<l1&&rb<l2)return 1;
    if(ra==rb)return 1;
    return 0;
}
bool cmp2(int l1,int r1,int l2,int r2){
    int ra=nxts[r1];
    int rb=nxtb[r2];
    if(ra<l1&&rb<l2)return 1;
    if(ra-l1==rb-l2)return 1;
    return 0;
}
int main(){
    /**读入&映射&造nxt**/ 
    fail[2]=1;
    for(int i=2,j;i<=m;i++){
        j=fail[i];
        while(j&&!cmp1(1,j,i-j+1,i))j=fail[j];
        fail[i+1]=j+1;
    }
    for(int i=1,j=1;i<=n;i++){
        while(j&&!cmp2(1,j,i-j+1,i))j=fail[j];
        j++;
        if(j==m+1){
            printf("%d",i-m+1);
            return 0;
        }
    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值