字符串匹配算法(BF算法&&KMP算法)

暴力匹配(BF)算法

BF算法,即暴力(Brute Force)算法,是普通的模式匹配算法,BF算法的思想就是将目标串S的第一个字符与模式串T的第一个字符进行匹配,若相等,则继续比较S的第二个字符和 T的第二个字符;若不相等,则比较S的第二个字符和T的第一个字符,依次比较下去,直到得出最后的匹配结果。BF算法是一种蛮力算法。

eg:
在这里插入图片描述
注:

  • 只要在匹配的过程当中,匹配失败,那么:i回退到刚刚位置的下一个,j回退到0下标重新开始。

代码如下:

class Solution {
    
    
public:    
  
    int strStr(string haystack, string needle) 
    {
    
    
        if(haystack.size()==0&&needle.size()==0)
            return 0;
        int i=0;     
        int j=0;
        while(i<haystack.size()&&j<needle.size())
        {
    
    
            if(haystack[i]==needle[j])
            {
    
    
                i++;
                j++;
            }
            else
            {
    
    
                i=i-j+1;
                j=0;
            }
        }
        if(j==needle.size())
            return i-j;
        else
            return -1;
    }
       
};

注:时间复杂度为O(m*n); m是主串长度,n是子串长度

KMP算法

KMP算法是一种改进的字符串匹配算法,由D.E.Knuth,J.H.Morris和V.R.Pratt提出的,因此人们称它为克努特—莫里斯—普拉特操作(简称KMP算法)。KMP算法的核心是利用匹配失败后的信息,尽量减少模式串与主串的匹配次数以达到快速匹配的目的。具体实现就是通过一个next()函数实现,函数本身包含了模式串的局部匹配信息。KMP算法的时间复杂度O(m+n)

区别:KMP 和 BF 唯一不一样的地方在,主串的 i 并不会回退,并且 模式串的j 也不会移动到 0 号位置

eg:

在这里插入图片描述

注:

  • 当匹配失败时,不进行主串的回退i,因为在这个地方匹配失败,说明i的前面和j的前面,是有一部分相同的,不然两个下标不可能走到这里来

next数组

KMP 的精髓就是 next 数组:也就是用 next[j] = k;来表示,不同的 j 来对应一个 K 值, 这个 K 就是你将来要移动的 j要移动的位置。

K 的值是这样求的:

  • 1、规则:找到匹配成功部分的两个相等的真子串(不包含本身),一个以下标 0 字符开始,另一个以 j-1 下标字符结尾。
  • 2、不管什么数据 next[0] = -1;next[1] = 0;在这里,我们以下标来开始,而说到的第几个第几个是从 1 开始;

求next数组的练习

  • 练习 1: 举例对于”ababcabcdabcde”, 求其的 next 数组?
    -1 0 0 1 2 0 1 2 0 0 1 2 0 0

  • 练习 2: 再对”abcabcabcabcdabcde”,求其的 next 数组? "
    -1 0 0 0 1 2 3 4 5 6 7 8 9 0 1 2 3 0

在这里插入图片描述
代码如下:

class Solution {
    
    
public:    
    void KMP(string haystack,string needle,int* next)
    {
    
       
        next[0]=-1;
        next[1]=0;
        int i=2;
        int k=0;
        int lenn=needle.size();
        while(i<lenn)
        {
    
    
            if(k==-1||needle[i-1]==needle[k])
            {
    
    
                next[i]=k+1;
                i++;   
                k++;
            }
            else
            {
    
    
                k=next[k];
            }
        }
    }
    int strStr(string haystack, string needle) 
    {
    
    
        if(haystack.size()==0&&needle.size()==0)
            return 0;
        int* next=new int[needle.size()];
        if(needle.size()>2)
        {
    
    
            KMP(haystack,needle,next);
        }
        if(needle.size()==1)
        {
    
    
            next[0]=-1;
        }
        if(needle.size()==2)
        {
    
    
            next[0]=-1;
            next[1]=0;
        }
        
        int i=0;
        int j=0;
        int lenh=haystack.size();
        int lenn=needle.size(); 
        while(i<lenh&&j<lenn)
        {
    
    
            if((j==-1)||(haystack[i]==needle[j]))
            {
    
    
                i++;
                j++;
            }
            else
            {
    
    
                j=next[j];
            }
        }
        if(j==lenn)
        {
    
    
            return i-j;
        }
        else
        {
    
    
            return -1;
        }
    }
};

注:

  • 这里的i是从2开始的,而i-1代表的才是已知的next[i-1]的值(next[i-1]=k)
  • 这样如果模式串中i-1位置的字符和k位置上的字符相同,则next[i]=k+1;否则k=next[k]

next数组的优化(nextval数组)

next 数组的优化,即如何得到 nextval 数组.nextval数组的求法很简单,如果当前回退到的位置,正好是和当前字符一样,那么就写那个字符的nextval值。不一样就写自己的

练习:
在这里插入图片描述

注:在写编程题时,next数组和nextval数组所得到的元素的值需要将每个元素都+1,所得的结果才是该编程题的答案

练习

leetcode 28. 实现 strStr()

注:该上面的代码就是该题的不同解法

猜你喜欢

转载自blog.csdn.net/AI_ELF/article/details/123613306