KMP字符串题解

KMP字符串题解

题目描述
给定一个模式串 S,以及一个模板串 P,所有字符串中只包含大小写英文字母以及阿拉伯数字。
模板串 P 在模式串 S 中多次作为子串出现。
求出模板串 P 在模式串 S 中所有出现的位置的起始下标。

输入格式
第一行输入整数 N,表示字符串 P 的长度。
第二行输入字符串 P。
第三行输入整数 M,表示字符串 S 的长度。
第四行输入字符串 S。
输出格式
共一行,输出所有出现位置的起始下标(下标从 0 开始计数),整数之间用空格隔开。

数据范围
1≤N≤105
1≤M≤106

样例
输入:
3
aba
5
ababa

输出:
0 2

时间复杂度
O=(2m)

#include <iostream>
using namespace std;
const int N=2e6+5;
int ne[N];
char p[N],s[N];
/*
ne数组存一下以每个位置结尾的“可匹配的最长前后缀的长度 ” 
例p[]={" aba"};这里从p[1]开始存储,个人习惯 
ne[1]   p[1]=a   因为需要滑动,那字符串就不能和自身相比
            所以只有一个字符的时候,无法移动            ne[1]为0 

ne[2]   p[1,2]=ab,前缀最多可能为a,后缀为b
                    a!=b,所以也无法移动                ne[2]=0; 

ne[3]  p[1,2,3]=aba    前缀可能为a,ab   后缀可能为a,ba;
                a==a,ab!=ba,所以最长匹配前后缀长度为 1  ne[3]=1 
 */ 
int main(){
    int n,m;
    cin>>n>>p+1;
    cin>>m>>s+1;//输入模式串,从下标1开始存储 
 //构建ne数组,ne的意思就是next,但next比较特殊,防止报错,就用ne代替 
    for(int i=2,j=0;i<=n;i++){ 
        while(j&&p[i]!=p[j+1]){//对模式串进行匹配 
            j=ne[j];//如果没匹配成功,j就变成ne[j]的位置 
        }
        //跳出循环一共有两种情况,j==0,一直匹配失败,ne数组从0开始重新计算
       //                       p[i]==p[j+1] 匹配成功 ne数组进入下一位计算 
        if(p[i]==p[j+1])
            j++;// 
        ne[i]=j;//匹配成功的i位与j位的对应关系 
    }
    //看看我们的ne数组,此步多余,就是单纯的看看ne数组 
//  for(int i=0;i<=n;i++){
//      cout<<ne[i]<<' ';
//  } 
        cout<<endl;


    //利用ne数组进行,模式匹配 
    for(int i=1,j=0;i<=m;i++){
        while(j&&s[i]!=p[j+1]){//对模式串进行匹配 
            j=ne[j];//匹配失败j到ne[j]的位置 
        }
        if(s[i]==p[j+1])
            j++;//匹配成功进入下一位 

        if(j==n){//完全匹配成功 
            cout<<i-n<<' ';//输出s数组中匹配成功字符串的首位置,即当前位置-模式串长度 
            j=ne[j];//寻找p在s中的下一次出现的位置,利用ne数组优化时间复杂度 
        }
    }

    /*
    最后看看时间复杂度
    匹配两个字符串
    1、从i=1的位置开始逐个匹配,利用ne数组减少比较次数
    2、i与j+1的位置不匹配(假设已知1~j匹配i-j~i-1)
    3、j调回ne[j]继续比较(因为 1~j匹配i-j~i-1所以1~ne[j]也能匹配到i-ne[j]~i-1)
    4、成功后依然j=ne[j],就是把这次成功当做失败,继续匹配下一个位置 

    因为j最多加m次 在加之前j每次都会减少且最少减一,j>0 
    所以while循环最多执行m次,如果大于m次,j会小于0 
    所以O(2m) 
    */ 

    /*
    给个样例大家试试
    输入 
    3
    aba               ->p数组 
    5
    ababa             ->s数组

    输出
    0 2 
    */
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值