字符串模式匹配pta(KMP)分析KMP算法

给定一个字符串 text 和一个模式串 pattern,求 pattern 在text 中的出现次数。text 和 pattern 中的字符均为英语大写字母或小写字母。text中不同位置出现的pattern 可重叠。

输入格式:

输入共两行,分别是字符串text 和模式串pattern。

输出格式:

输出一个整数,表示 pattern 在 text 中的出现次数

 输入样例1:

zyzyzyz
zyz

输出样例1: 

AABAACAADAABAABA
AABA

 输出:

3

数据范围与提示:

1≤text, pattern 的长度 ≤106, text、pattern 仅包含大小写字母。

分析思路

笔者刚开始学习KMP算法时看了很多文章,教程还是不太懂,现在总算是弄懂了。

为了避免朴素算法的低效,所以前辈们设计出了一个模式匹配算法,可以大大避免重复遍历的情况即KMP算法

我认为KMP算法的关键在于对要查找的字符串在比较前先做一个分析,减少查找难度。遍历分析这个字符串,得到当前字符串的前后缀相似度,将其保存到一个数组中, 即next数组。

得到当前字符串的前后缀相似度的目的是:在主串和匹配子串遍历到不匹配时,从头回溯,可以从当前不匹配的位置i,的第next[i]处进行查找。

                  123456789

例如主串:abcababca

       子串:abcabx

当遍历到第六个a不等于x时,由于前后缀相似度x前的4.5.位ab等于前1.2位ab(子串4.5位匹配主串4.5位,即1.2.位也与主串4.5位匹配)所以回溯成主串的第6位与子串第三位比较

子串回溯的位置就由next数组中的前后缀相似度决定

void kmp(){
	int i=0,j=0;
	while(j<t.size()) //遍历主串
{
		if(t[j]==s[i]||i==-1){//如果主串与子串当前位置匹配,或为子串第一个位置
			i++;   //子串向后一位
			j++;   //主串向后一位
		}
		else {
			i=Next[i];  //不匹配,去找子串中当前位置对应next数组中存放的值即另一个子串位置是否与主串匹配
		}
		if(i==s.size()){//匹配完成
			ans++;   //匹配完成次数+1
		}		
	}
}

那要怎么得出next数组呢

见详细注释

void getNext()
{
	int j =0;//表示后缀位置
    int k =-1;//表示前缀遍历到的位置

    Next[0]=-1;   //第一位标记为0
        while(j < s.size()) //遍历子串
   {
            if(k ==-1|| s[j]== s[k])  //当前子串是初始值位置或前后缀相同
         {
            j++;  //后缀位置+1
            k++;  //前缀匹配到的位置+1
            Next[j]= k;
         }
        else
        {
        	k =Next[k];   //不同从存放的next数组中找当前前缀位置的前缀开始比较
                          //可以理解为使前缀短一点比较看看
		}     
     }  
}

完整代码含主函数

#include<bits/stdc++.h>

using namespace std;
int Next[100001],ans=0;
string s,t;

void getNext()
{
	int j =0;//表示后缀位置
    int k =-1;//表示前缀遍历到的位置

    Next[0]=-1;   //第一位标记为0
        while(j < s.size()) //遍历子串
   {
            if(k ==-1|| s[j]== s[k])  //当前子串是初始值位置或前后缀相同
         {
            j++;  //后缀位置+1
            k++;  //前缀匹配到的位置+1
            Next[j]= k;
         }
        else
        {
        	k =Next[k];   //不同从存放的next数组中找当前前缀位置的前缀开始比较
                          //可以理解为使前缀短一点比较看看
		}     
     }  
}

void kmp(){
	int i=0,j=0;
	while(j<t.size()) //遍历主串
{
		if(t[j]==s[i]||i==-1){//如果主串与子串当前位置匹配,或为子串第一个位置
			i++;   //子串向后一位
			j++;   //主串向后一位
		}
		else {
			i=Next[i];  //不匹配,去找子串中当前位置对应next数组中存放的值即另一个子串位置是否与主串匹配
		}
		if(i==s.size()){//匹配完成
			ans++;   //匹配完成次数+1
		}		
	}
}
int main ()
{
	cin>>t>>s;
	getNext();
	kmp();
	printf("%d",ans);
	return 0;
}

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值