字符串匹配算法KMP

KMP算法解决的问题

字符串str1和str2,str1是否包含str2,如果包含返回str2在str1中开始的位置。

如何做到时间复杂度O(N)完成?

最长前缀后缀匹配长度k:

k一定小于字符串长度,因为k==字符串长度时就是比较两个字符串本身,一定相等,没有意义

求str2的next数组:

next[i]表示str[0]~str[i-1]的最长前缀后缀匹配k。由于next[0]前面没字符串,人为规定next[0]=-1

经典过程:

如果str1和str2前面都相等,str1比较到最后是X,str2比较到最后时Y,不相等:

str1由X位置跳回到i+1位置,str2由Y位置跳回到0位置

KMP:

如果str1和str2前面都相等,str1比较到最后是S,str2比较到最后是Z,不相等:

str1位置不变,还指向S,str2指向str2[ next[最后] ]

两个关键点:

1)此时str1以 i ~ j - 1 范围为起点配str2是一定配不出来的

2)此时实际上是str1以j为起点,str2以0为起点开始配,但是因为三个框字符串相同,因此str1位置不变,还指向结尾,str2指向str2[ next[最后] ]

证明关键点1):

假设此时str1以 i ~ j - 1 范围中有位置k,以k为str1的起点,可以配出str2。那么由k到X-1这一段一定与str从0开始的一段相等,也就是说,str2从0开始更长的一段和str2以Y结尾更长的一段相等,则str2的位置Y找到了一个更大的最长前缀后缀匹配长度,与已知条件矛盾。

在查找过程中str1一直往前走,str2依据next数组依次往前找,如果在0位置还找不到,str1往前走

时间复杂度:O(N)

求next数组:

动态规划,比较i - 1位置与cn位置,如果相同,next[i] = cn + 1 ; i++ ; cn++ ; 否则cn = next[cn],cn == 0时,next[i] = 0 ; i++;

时间复杂度:O(M)     M为str2长度

vector<int> getNext(string s){
    vector<int> ans (s.size(), -1);
    if(s.size() == 1){
        return ans;
    }
    ans[0] = -1;
    ans[1] = 0;
    int i = 2;
    int cn = 0;
    while(i < ans.size()){
        if(s[cn] == s[i - 1]){
            ans[i] = cn + 1;
            i++;
            cn++;
        }
        else if(cn > 0){
            cn = ans[cn];
        }
        else{
            ans[i] = 0;
            i++;
        }
    }
    return ans;
}

int kmp(string str1, string str2){
    if(str1.empty() || str2.empty() || str1.size() < str2.size()){
        return -1;
    }
    int i1 = 0;
    int i2 = 0;
    vector<int> next = getNext(str2);
    while(i1 < str1.size() && i2 < str2.size()){
        if(str1[i1] == str2[i2]){
            i1++;
            i2++;
        }
        else if(i2 != 0){
            i2 = next[i2];
        }
        else{
            i1++;
        }
    }
    return i2 == str2.size() ? i1 - i2 : -1;
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

芜湖高学成

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

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

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

打赏作者

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

抵扣说明:

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

余额充值