c++ 模式匹配 KMP算法

朴素的模式匹配

#include <iostream>
#include <string>
using namespace std;
 
int Match1(string str, string searchStr)
{
	int i = 0;
	int j = 0;
	while (i < str.size() && j < searchStr.size())
	{
		if (str[i] == searchStr[j])
		{
			i++;
			j++;
		}
		else//指针回退,重新开始匹配
		{
			i = i - j + 1;//目标串回到匹配位置的下一位置
			j = 0;//模式串回到起始0位置
		}
	}
	if (j >= searchStr.size())
	{
		return i - searchStr.size();//返回第一次匹配的首地址
	}
	else
	{
		return -1;
	}
}
int main(){
	string s = "goodgoogle";
	string t = "google";
	cout << Match1(s, t)<<endl;
}

参考:【数据结构】模式匹配算法
这种多数情况下m<<n所以认为时间复杂度是 O(m*n)
n:目标串长度
m:模式串长度
慢的原因是每次只向前一次,目标串的检测指针要回退

KMP算法

把模式匹配的时间复杂度降到O(n)
最关键的一步是nextKMP算法—终于全部弄懂了

void Getnext(int next[],String t)
{
   int j=0,k=-1;
   next[0]=-1;
   while(j<t.length-1)
   {
      if(k == -1 || t[j] == t[k])
      {
         j++;k++;
         next[j] = k;
      }
      else k = next[k];//此语句是这段代码最反人类的地方,如果你一下子就能看懂,那么请允许我称呼你一声大神!
   }
}

这个博主说下面那行代码不太理解,但是我愚笨的脑袋连上面那些也不理解
主要是开始没看懂k是什么,next(j)是容易理解的

在这里插入图片描述
我们定义next是一个这样的辅助向量(其实可以理解为数组),和

这种信息就是对于每模式串 t 的每个元素 t j,都存在一个实数 k ,使得模式串 t 开头的 k 个字符(t 0 t 1…t k-1)依次与 t j 前面的 k(t j-k t j-k+1…t j-1,这里第一个字符 t j-k 最多从 t 1 开始,所以 k < j)个字符相同。如果这样的 k 有多个,则取最大的一个。模式串 t 中每个位置 j 的字符都有这种信息,采用 next 数组表示,即 next[ j ]=MAX{ k }。
————————————————
版权声明:本文为CSDN博主「June·DD」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/dark_cy/article/details/88698736

是一样的

为什么k要<j-1?
要不next肯定是j-1,下次比较还是从pj比较就没有意义了

但是循环过程中的k是什么?

void Getnext(int next[],String t)
{
   int j=0,k=-1;
   next[0]=-1;
   while(j<t.length-1)
   {
      if(k == -1 || t[j] == t[k])
      {
         j++;k++;
         /*这里是先往下走一个下标,
         再把k++也就是说实际上是确认的是j下一个位置的next值,所以上面while里也写的是j<t.length-1*/
         next[j] = k;
      }
      else k = next[k];
   }
}

但是之前的问题还没解决,而和else那句类似的问题,我们确定了一个next[j]为什么k不回退,k是什么东西.
我们先把next[0] = k

void Getnext(int next[],String t)
{
   int j=0,k=-1;
   next[0]=k;//实际上可以替换为k
   while(j<t.length-1)
   {
      if(k == -1 || t[j] == t[k])
      {
         j++;k++;
         /*这里是先往下走一个下标,
         再把k++也就是说实际上是确认的是j下一个位置的next值,所以上面while里也写的是j<t.length-1*/
         next[j] = k;
      }
      else k = next[k];
   }
}

那么我们可以发现事实上每一个k都会对应一个next值,

  1. next[j] = k;时k是tj前k个元素和t0- t k-1匹配
    这个时候如果t[j] == t[k]那么tj+1 的前k+1个元素和t0- t k 匹配
    k实际上就是从现在当前处理的tj开始向前k个(包括tj)和t0 - tk+1匹配
  2. else k = next[k];这里的k其实和主函数里原理相同,相当于是从头开始的模式匹配
    (以前半部分做主串,正在匹配的字符是模式串),那么这句话的意思就很清楚了:k回退到 next[k]的位置继续尝试和t~j~进行匹配,

改进

void Getnext(int next[],String t)
{
   int j=0,k=-1;
   next[0]=-1;
   while(j<t.length-1)
   {
      if(k == -1 || t[j] == t[k])
      {
         j++;k++;
         if(t[j]==t[k])//当两个字符相同时,就跳过
            next[j] = next[k];
         else
            next[j] = k;
      }
      else k = next[k];
   }
}

如果t[j]==t[k]
那么tj匹配不上tk肯定也匹配不上

全部代码leetcode 28
这个题暴力也不会超时,但是可以学习kmp算法

int* getNext( string needle)
    {
        int n = needle.size();
        int* next = new int[n+10];
        int k = -1;
        next[0] = k;
        int i = 0;
       while(i<n)
        {
            if(k== -1 ||needle[i]==needle[k])
            {
                i++;k++;
                if(t[j]==t[k])//当两个字符相同时,就跳过
            			next[j] = next[k];
         			else
            			next[j] = k;
          
               
            }
            else
            {
                k = next[k]; 

            }
        }
        return next;

    }
    int strStr(string haystack, string needle) {
        int* next =getNext(needle);
        int i=0,j=0,n=haystack.size(),ans=-1,p=needle.size();

        while(i<n&&j<p)
        {
            
            
            if(j==-1||haystack[i]==needle[j])
            {
                i++,j++;
            }
            else
            {
                j= next[j];
            }
        }
    if(j==p)
        {
                
                return  i-j;
        }
        return ans;
    }
    
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值