前言

现在有两个字符串:\(s1\)和\(s2\),现在要你输出\(s2\)在\(s1\)当中每一次出现的位置,你会怎么做?

暴力匹配算法

基本思路

用两个指针分别指向当前匹配到的位置,并对当前状态进行分类讨论:若相同则继续往下匹配,否则回溯

大致思路

用\(i\)来存储\(s1\)当前匹配到的位置,用\(j\)来存储\(s2\)当前匹配到的位置,则可得初始状态下\(i=j=0\)。

对于当前状态,有两种可能性:

①:\(s1[i]==s2[j]\)。则\(i++,j++\)

②:\(s1[i]!=s2[j]\)。则\(i-=(j-1),j=0\)

评价

时间复杂度:\(O(nm)\)。显然,这个方法效率并不高,每一次回溯要耗去大量时间,能不能进行优化呢?

\(KMP\)算法

简介

\(KMP\)算法是对暴力匹配算法的改进,由\(D.E.Knuth\),\(J.H.Morris\)和\(V.R.Pratt\)同时发现,因此人们称它为\(Knuth-Morris-Pratt\)算法(简称\(KMP\)算法)。

基本思路

\(KMP\)算法的关键是利用匹配失败后的信息,尽量减少模式串与主串的匹配次数以达到快速匹配的目的。具体实现就是实现一个\(Next\)函数,函数本身包含了模式串的局部匹配信息。

大致思路

还是用\(i\)来存储\(s1\)当前匹配到的位置,用j来存储\(s2\)当前匹配到的位置,则可得初始状态下\(i=j=0\)

对于当前状态,有两种可能性:

①:\(s1[i]==s2[j]\)。则\(i++,j++\)

②:\(s1[i]!=s2[j]\)。则\(j=Next[j]\)(i不变)

其中\(Next\)数组存储的是当前这一位的部分匹配值(这在后面会详细介绍),所以只要让\(j\)变成\(Next[j]\),就可以继续对当前字符串进行匹配了,省去了i回溯所耗去的大量时间

\(Next\)数组

在匹配过程中,你可以发现一个基本事实是:当\(s1[i]\)与\(s2[j]\)不匹配时,你其实知道前面\(j-1\)字符是什么。

\(KMP\)算法的想法是,设法利用这个已知信息,不要把"搜索位置"移回已经比较过的位置,继续把它向后移,这样就提高了效率。

所以,我们就可以把当前所得到的部分匹配值给求出来。又由于对于同一个字符串,部分匹配值是固定不变的,所以可以把它存在\(Next\)数组里。

那么\(Next\)数组怎么求呢?

记得某大佬说过这样一句话:

\(Excerpt\)

求\(Next\)数组的过程就是一个\(KMP\)的过程。

首先,令\(i=0\),\(j=-1\),\(Next[0]=-1\),且当前要求的是\(Next[i+1]\)。则对于当前状态,有两种可能性:

①\(j==-1\)或\(s2[i]==s2[j]\)。则\(i++,j++,Next[i]=j\)

②\(j!=-1\)且\(s2[i]!=s2[j]\)。则\(j=Next[j]\)//把\(j\)赋值为\(j\)的部分匹配值

这样就可以轻松求出\(Next\)数组了。

代码

#include<bits/stdc++.h>
#define N 1000000
#define pc(ch) (pp_<100000?pp[pp_++]=ch:(fwrite(pp,1,100000,stdout),pp[(pp_=0)++]=ch))
int pp_=0;char pp[100000];
using namespace std;
int len1,len2,Next[N+5];//len1存储s1的长度,len2存储s2的长度,这样不用调用strlen(),strlen()会超时;Next[]存储部分匹配值
char s1[N+5],s2[N+5];
inline void write(int x)
{
if(x>9) write(x/10);
pc(x%10+'0');
}
inline void GetNext()//求出Next[]数组
{
register int i=0,j=Next[0]=-1;//初始化
while(i<=len2)//类似于一个KMP的过程
{
if(j==-1||s2[i]==s2[j]) i++,j++,Next[i]=j;
else j=Next[j];
}
}
int main()
{
register int i=0,j=0;
scanf("%s%s",s1,s2),len1=strlen(s1),len2=strlen(s2),GetNext();
while(i<=len1)//KMP的过程
{
if(j==-1||s1[i]==s2[j]) {++i;if(++j==len2) write(i-len2+1),pc('\n'),j=Next[j];/*如果找到答案就输出*/}
else j=Next[j];//如果匹配失败,就更新j为其部分匹配值
}
for(i=1;i<=len2;++i) write(Next[i]),pc(' ');//依照题意输出Next[]数组
return fwrite(pp,1,pp_,stdout),0;
}

从暴力匹配到KMP算法的更多相关文章

  1. 字符串查找算法总结(暴力匹配、KMP 算法、Boyer-Moore 算法和 Sunday 算法)

    字符串匹配是字符串的一种基本操作:给定一个长度为 M 的文本和一个长度为 N 的模式串,在文本中找到一个和该模式相符的子字符串,并返回该字字符串在文本中的位置. KMP 算法,全称是 Knuth-Mo ...

  2. 算法之暴力破解和kmp算法 判断A字符串是否包含B字符串

    我们都知道java中有封装好的方法,用来比较A字符串是否包含B字符串 如下代码,contains,用法是 str1.contains(str2), 这个布尔型返回,存在返回true,不存在返回fals ...

  3. 串的匹配:朴素匹配&amp;KMP算法

    引言 字符串的模式匹配是一种经常使用的操作. 模式匹配(pattern matching),简单讲就是在文本(text,或者说母串str)中寻找一给定的模式(pattern).通常文本都非常大.而模式 ...

  4. 软件设计师_朴素模式匹配算法和KMP算法

    1.从主字符串中匹配模式字符串(暴力匹配) 2. KMP算法

  5. KMP算法-字符匹配

    字符匹配模式-KMP算法 j直接跳到了2的位置,因为在之前的都相同. 那么就需要求如果不等了之后,j需要回跳的位置next[j] 如果tk'与tj相等,则next [j+1]=k'+1 如果tk'与t ...

  6. KMP算法(研究总结,字符串)

    KMP算法(研究总结,字符串) 前段时间学习KMP算法,感觉有些复杂,不过好歹是弄懂啦,简单地记录一下,方便以后自己回忆. 引入 首先我们来看一个例子,现在有两个字符串A和B,问你在A中是否有B,有几 ...

  7. 解读KMP算法

    前后断断续续搞了5个月,每次都以为自己懂了, 但是要写的时候都不知从何下手,然后又是各种找博客,看帖子,所以这次试着用自己的语言写一个博客. 首先,KMP算法就是从一个模板字符串(S) 中匹配目标字符 ...

  8. 数据结构与算法JavaScript (五) 串(经典KMP算法)

    KMP算法和BM算法 KMP是前缀匹配和BM后缀匹配的经典算法,看得出来前缀匹配和后缀匹配的区别就仅仅在于比较的顺序不同 前缀匹配是指:模式串和母串的比较从左到右,模式串的移动也是从 左到右 后缀匹配 ...

  9. 完全掌握KMP算法思想

    文档下载页面http://download.csdn.net/detail/yedeqixian/4209500      80页在讲KMP算法的开始先举了个例子,让我们对KMP的基本思想有了最初的认 ...

随机推荐

  1. PAT12-012【建最小堆】

    卧槽..没看清 值 还有负的.. PS: 注意他说是一个一个插入的,也就是插在完全二叉树的最末位置,然后向上更新. #include<bits/stdc++.h> using namesp ...

  2. 白白的(baibaide)——树状数组套主席树+splay

    题目 [题目描述] 有一个长度为 $n$ 的序列 $a_1, a_2, \dots, a_n$,一开始每个位置都是白色.如果一个区间中每个位置都是白色,则称这是一个白白的区间.如果一个白白的区间向左或 ...

  3. 3、kubernetes应用快速入门190625

    一.kubernetes应用入门 1.kubectl命令 Basic Commands create Create a resource from a file or from stdin. expo ...

  4. Eclipse中新建Maven Web项目报错:The superclass "javax.servlet.http.HttpServlet" was not found on the Java Build Path

    在maven web项目中的index.jsp中的错误信息如下: The superclass "javax.servlet.http.HttpServlet" was not f ...

  5. spring /* 和 /** 的 区别。

    例如 /** 拦截 /index/1   和  /index /* 代表 /index/1 而  /index 则不会被拦截

  6. [Leetcode]009.Palindrome Number

    public class Solution { public boolean isPalindrome(int x) { if (x<0 || (x!=0 && x%10==0) ...

  7. Luogu P2480 [SDOI2010]古代猪文 卢卡斯+组合+CRT

    好吧刚开始以为扩展卢卡斯然后就往上套..结果奇奇怪怪又WA又T...后来才意识到它的因子都是质数...qwq怕不是这就是学知识学傻了.. 题意:$ G^{\Sigma_{d|n} \space C_n ...

  8. chapter05_20180330

    // 5.7 主构造器// 主构造器的参数直接旋转在类名之后class Person57(val name: String, val age: Int) { // 上边()中的内容就是主构造器的参数} ...

  9. Java面向对象_对象数组

    今天学习了对象数组,写点东西总结一下.废话不多说,啥是对象数组呢? 对象数组的概念是这么讲的,对象数组就是数组里的每个元素都是类的对象,赋值时先定义对象,然后将对象直接赋给数组. 举个例子,使用对象数 ...

  10. Microsoft JET Database Engine (0x80004005)未指定的错误解决

    Microsoft JET Database Engine (0x80004005)未指定的错误,这个错误只有在使用Access数据库时才能出现 出现以上问题,可以使用以下步骤进行解决问题: 1.系统 ...