KMP算法

#include<iostream>
using namespace std;
#include<string>
class KMP{
public:
    KMP(int n,string s0,string p0):s(s0),p(p0),length(n){
        next=new int[n];
        CalNext();
    }
    ~KMP() {delete[] next;}
    void TargetPosition(){//直接输出位置
        int j=0;
        int k=s.size();
        for(int i=0;i<k;){
            while(p[j]==s[i]&&i<k&&j<length){
                ++i;++j;
            }
            if(j==0) ++i;//如果发现p[0]!=s[i],头一个就匹配不了了,往后看++i;
            if(j==length){//如果j==length 说明匹配成功
                cout<<i-j+1<<endl;
            }
            if(j>=1)//如果j!=0,不管匹配成功 还是匹配失败,都要继续跟s[i]匹配,那就看
                    //next[j-1] 前后缀最长下表是多少 +1继续匹配
                j=next[j-1]+1;
        }
        OutputNext();
        return;
    }
    void OutputNext(){
        for(int i=0;i<length;++i)
            cout<<next[i]+1<<' ';
        return ;
    }
private:
    void CalNext(){//求出前缀最长下标 存入next;
        next[0]=-1;
        for(int i=1;i<length;++i){
            int j=next[i-1];
            while(j>=0&&p[j+1]!=p[i])
                j=next[j];
            if(p[j+1]==p[i])
                next[i]=j+1;
            else
                next[i]=-1;
            }
        return;
    }
    int *next;
    int length;
    string s;//目标串
    string p;//模式串
};
int main(void){
    string s;
    string p;
    cin>>s;
    cin>>p;
    KMP kmp(p.size(),s,p);
    kmp.TargetPosition();
}

①求string的长度用 size()。

string p;

p.size() 是p的长度.

②第一次错误点:当p[0]!=s[i]的时候 没有进行++i,导致死循环。

②差点就错误:i的最终取值是i<s.size(),而不是i<=s.size()-p.size();

因为最终匹配,仍然需要匹配到最后一位,所以i别越界就可以了。


一、next数组的求解

我们这里的next数组 存的是最长前后缀匹配的最大下标。

前后缀的长度必然要小于字符串的长度,

所以next[0]必然<0,所以next[0]=-1;相当于没有前后缀相等,最大下标为数组的前一个位置。

然后对i=1到i=length-1的求解用到递归求法。

如果不用递归求法当然也可以,不过费时。

通过代码来看求解方法:

    void CalNext(){//求出前缀最长下标 存入next;
        next[0]=-1;//第一位没有前后缀赋值为-1;
        for(int i=1;i<length;++i){//从第二位求到最后一位,利用递推关系
            int j=next[i-1];//j表示第i位前面最长前后缀的下标
            while(j>=0&&p[j+1]!=p[i])//如果最长前后缀的后一位刚好等于第i位,说明此时第i位前后缀
                                    //就是j+1;
                j=next[j]; //不然的话,继续往下看能不能找到满足前后缀又有后一位刚好等于i的
                           //实在没有 那么j==-1时停止, if(p[-1+1]==p[i]) next[i]=0;
            if(p[j+1]==p[i])
                next[i]=j+1;
            else
                next[i]=-1;
            }
        return;
    }注意next数组的值最小为-1,所以不用怕它超出。

二、用前后缀可以跳转的原理

123456789

abcdefdaX······(目标串)

abcdefdaaetgs;

假设从第2号位置,从头比是有效的,

则必有,bcdefda==abcdefd  

也就是说 abcdefda 的前后缀最长长度应当是7,如果是7 从头开始比才能保证,这些比较一直到X是有效的,然而如果不是7,必然无效。

怎么知道最长是不是7?  我们在求next函数的时候就已经保证了,我们已经保证了next数组是最长的长度,所以用next数组从前后缀最长的地方比较才是有意义的,否则必然是没有意义的。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
好的,下面我来为您讲解使用KMP算法解决字符串匹配问题的方法。 KMP算法是一种比较高效的字符串匹配算法,它的核心思想是利用已经匹配过的信息,尽可能减少不必要的字符比较次数,从而提高匹配效率。具体实现时,KMP算法利用前缀和后缀的概念,对于每个字符,计算出它的最长前缀和最长后缀的匹配长度,然后根据这些信息来移动模式串,从而减少比较次数。 下面是使用KMP算法解决字符串匹配问题的代码实现: ```python def str_str(s, n): if not n: return 0 if not s: return -1 m, k = len(s), len(n) next = get_next(n) i = j = 0 while i < m and j < k: if j == -1 or s[i] == n[j]: i += 1 j += 1 else: j = next[j] if j == k: return i - k else: return -1 def get_next(n): k, j = -1, 0 next = [-1] * len(n) while j < len(n) - 1: if k == -1 or n[k] == n[j]: k += 1 j += 1 next[j] = k else: k = next[k] return next ``` 需要注意的是,KMP算法中的next数组表示的是模式串中每个位置的最长前缀和最长后缀的匹配长度,而不是暴力匹配算法中的每个位置的最长前缀和最长后缀。因此,KMP算法中的next数组需要先计算出来,然后再根据它来移动模式串。 接下来,我来给您提供一组测试用例,证明KMP算法的正确性: ```python assert str_str("hello", "ll") == 2 assert str_str("aaaaa", "bba") == -1 assert str_str("mississippi", "issip") == 4 ``` 这些测试用例可以验证KMP算法的正确性。相比暴力匹配算法KMP算法的时间复杂度为O(m+n),可以在较短的时间内解决字符串匹配问题。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Yorelee.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值