【KMP算法】时间复杂度O(N)的字符串匹配算法

目录

案例:假定我们给出字符串 ”ababcabcdabcde”作为主串, 然后给出子串: ”abcd”,现在我们需要查找子串是否在主串中 出现,出现返回主串中的第一个匹配的下标,失败返回-1 ;

1.BF算法(暴力算法)

2.KMP算法

1.next数组

 2.代码中如何实现next数组;

 3.代码实现

4.next数组优化


案例:假定我们给出字符串 ”ababcabcdabcde”作为主串, 然后给出子串: ”abcd”,现在我们需要查找子串是否在主串中 出现,出现返回主串中的第一个匹配的下标,失败返回-1 ;

1.BF算法(暴力算法)

思路:遍历主串,子串和当前位置的比较;匹配成功返回主串当前位置,主串遍历完成都没有匹配成功,返回-1;

代码:

#include<iostream>
#include<string>
using namespace std;

int BF(string str,string sub)
{
    int strlen=str.size();
    int sublen=sub.size();
    //遍历主串,和子串比较
    for(int i=0;i<strlen-sublen+1;i++)
    {
        int t=i,j=0;
        for(;j<sublen;j++)
        {
            if(str[t++]!=sub[j])
                break;
        }
        if(j==sublen)
            return i;
    }
    return -1;
}
int main()
{
    cout<<BF("ababcabcdabcde","abcd")<<endl;
    return 0;
}

结果:

时间复杂度:O(N^2) (或者O(strlen*sublen))  空间复杂度:O(1)

2.KMP算法

概念:KMP算法的核心是利用匹配失败后的信息,尽量减少模式串与主串的匹配次数以达到快速匹配的目的。具体实现就是通过一个next()函数(数组)实现,函数本身包含了模式串的局部匹配信息。KMP算法的时间复杂度O(m+n) [1] (看不懂看下面的例子)

思想:在子串和主串不匹配,两者前面的元素一定相同

  • 如果主串当前位置前n个和子串的前n个相同,让子串以O(1)时间复杂度返回到相同部分的下一个位置

结合子串和主串前面元素相同,实际找子串的前n项和后n项也是一样效果

1.next数组

next数组:next【0】设置为-1,是否能在当前位置前面找到两个以第一个元素开始,(当前位置-1)元素结束的字符串,如果找得到当前位置的next【i】为第一个字符串最后一个元素(子串下标是-1的,next[i]==2, 子串实际是第3个元素);找不到next【i】=0(子串下标-1,实际是第一个元素);

 

 2.代码中如何实现next数组;

  1. next【0】=-1,next【1】=0(前面只有一个元素);
  2. 当前位置i,如果前一个位置,子串【i-1】等于子串【next【i-1】】,当前位置next【i】=next[i-1]+1;
  3. 子串【i-1】不等于子串【next【i-1】】,重复这个过程,一直没有等于最后会到第一个元素,next【第一个元素】值为-1,当前位置next【i】=-1+1;

 3.代码实现

  • KMP算法在字符串匹配中的时间复杂度:O(N)   空间复杂度:O(N)
#include<iostream>
#include<string>
#include<vector>
using namespace std;
    
    void BuildNextFunc(vector<int> &next, string &sub)
    {
        next[0] = -1;
        if(sub.size() > 1)
            next[1] = 0;
            
        int i = 2;//下一项
        int k = 0;//前一项的K
        while(i < sub.size())//next数组还没有遍历完
        {
            if((k == -1) || sub[k] == sub[i-1])//
            {
                next[i] = k+1;
                i++;
                k++;//k = k+1???//下一个K的值新的K值
            }
            else
            {
                k = next[k];
            }
        }
    }
    int KMP(string &str, string &sub)
    {
        if(str.empty() || sub.empty())
            return -1;

        //构建next数组
        int n = sub.size();
        vector<int> next(n, 0);
        BuildNextFunc(next, sub);

        int subCur = 0, strCur = 0;
        while(strCur < str.size() && subCur < n)
        {
            if(subCur == -1 || sub[subCur] == str[strCur]){
                subCur++;
                strCur++;
            }
            else
            {
                subCur = next[subCur];
            }
        }
        if(subCur >= n)
            return strCur - subCur;
        return -1;
    }
int main()
{
    cout<<KMP("ababcabcdabcde","abcd")<<endl;
    cout<<KMP("ababcabcdabcde","")<<endl;
    cout<<KMP("ababcabe","abcd")<<endl;
    return 0;
}

4.next数组优化

next 数组的优化,即如何得到 nextval 数组:有如下串: aaaaaaaab,他的 next 数组是-1,0,1,2,3,4,5,6,7. 而修正后的数组 nextval 是-1, -1, -1, -1, -1, -1, -1, -1, 7;

伪代码:sub[i]==sub[  next[i]  ],nextval[i]=nextval[  next[i]  ]

为什么可行:KMP算法就是如果不相同子串到next【i】去,然后继续比较,当前位置是i,变到next【i】,i位置就是因为值和主串的值不同才到next【i】位置的,现在i和next【i】的值相同,还得比较后继续next【i】到next【next【i】】

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值