【超级易懂】经典算法KMP与next数组含义

本文介绍了在解决蓝桥杯每日一题中的字符串匹配问题时,如何从暴力遍历优化到使用KMP算法,通过KMP数组记录前后缀匹配信息,降低时间复杂度,解决ABA问题的实例。
摘要由CSDN通过智能技术生成

蓝桥杯每日一题




一、题目

问题描述
在这里插入图片描述

输入格式
在这里插入图片描述

输出格式
在这里插入图片描述
在这里插入图片描述

二、解题

1.思路

最朴素的思想是用两层循环遍历s1和s2,一次次匹配直到成功。
但这里会出现一个问题,假设s1=ABABABA,s2=ABA,第一次匹配完成,i到达3,此时会忽略i=2时也能匹配的情况,如果每次i都要回退到i-=s2.size()+2的位置,时间复杂度会非常高,属于暴力解题。

#include<string>
#include<iostream>
using namespace std;
int main() {
	string s1;
	string s2; 
    cin >> s1;
    cin >> s2;
    int i;
	int j;
	for(i=0;i<s1.size();++i){
    	for(j=0;j<s2.size();++j){
    		if(s2[j]==s1[i]){
    			if(j==s2.size()-1){
    				i=i-s2.size()+2;
    				cout<<i;
    				break;
				}
			}
			else{
				j=-1;
			}
			i++;
		}
	}	
    return 0;
}

在这里插入图片描述
如果s1的ABA中,重复的前缀和后缀的间隔能够记录下长度len,这样就可以判断上述的第一次匹配完成之后问题,i应该回退到i-len的位置。
找前后缀的过程就是KMP算法的核心省时过程。这个算法还厉害在,他还考虑了i在任意位置匹配失败的时候的len值,并且可以多次回跳。用数组KMP[]记录。

2.代码

代码摘自洛谷
KMP数组,也就是next数组,下标的意义是匹配到模式串的第几位,索引是前面包括当前都匹配成功的情况下,下一位不匹配时,将模式串回退到索引值作为的新的下标的位置,继续与字符串判断。
例如:

下标012345
模式串ABCABD
索引000120

如果我们令i为前缀尾,j为后缀尾

	int i;
	int j;
	int KMP[1000];
	i=0;//前缀尾 
	j=1;//后缀尾
	KMP[0]=0;//第二位匹配失败,模式串要跳回下标0,很容易理解
	while(j<s2.size()){
		//不相等时根据上一位记下的当前位不等时该跳去的地方跳转
		//一直跳到能匹配或者干脆跳回0了 
		while(i>0&&s2[i]!=s2[j])i=KMP[i-1];
		//匹配成功,i++,j++(循环)
		if(s2[i]==s2[j]){
			i++;
		}
		//i不仅代表着回调的索引,其实也代表着从第一位到前缀尾这段字符串的最长相同前后缀
		KMP[j]=i;
		j++;
	}

核心代码:

    j=0;
    for(i=0;i<s1.size();i++){
    	if (s2.size() == 0) {
            return 0;
        }

      while(j&&s2[j]!=s1[i])j=KMP[j-1];//如果失配 ,那么就不断向回跳,直到可以继续匹配 
	  
      if (s2[j]==s1[i]) j++;//如果匹配成功,那么对应的模式串位置++ 
      
      if (j==s2.size()) //一次匹配完了,假装这次匹配失败,继续往后匹配
	  {
		  cout<<i-s2.size()+1+1<<endl;
		  j=KMP[j-1];
	  }
   }

全部:

#include<string>
#include<iostream>
using namespace std;
int main() {
	string s1;
	string s2; 
    cin >> s1;
    cin >> s2;
    int i;
	int j;
	int KMP[1000];
	i=0;//前缀尾 
	j=1;//后缀尾
	KMP[0]=0;
	while(j<s2.size()){
		//不相等时根据上一位记下的当前位不等时该跳去的地方跳转
		//一直跳到能匹配或者干脆跳回0了 
		while(i>0&&s2[i]!=s2[j])i=KMP[i-1];
		if(s2[i]==s2[j]){
			i++;
		}
		KMP[j]=i;
		j++;
	}
	j=0;
    for(i=0;i<s1.size();i++){
    	if (s2.size() == 0) {
            return 0;
        }

      while(j&&s2[j]!=s1[i])j=KMP[j-1];//如果失配 ,那么就不断向回跳,直到可以继续匹配 
	  
      if (s2[j]==s1[i]) j++;//如果匹配成功,那么对应的模式串位置++ 
      
      if (j==s2.size()) //继续匹配 
	  {
		  cout<<i-s2.size()+1+1<<endl;
		  j=KMP[j-1];
	  }
   }
   for(i=0;i<s2.size();i++){
   		if(i==0){
   			cout<< KMP[i];
		}
		else{
			cout<<" "<< KMP[i];
		}
   	 
   }
    return 0;
}

总结

一个小BUG能改一年,我真的服了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值