字符串匹配的暴力解法

给定字符串s和p,寻找字符串p在字符串s中出现的位置,暴力解法如下所示:

  • 如果当前字符匹配成功,++i;++j,继续匹配下一字符。
  • 如果s[i]s[j]匹配失败,令i-=(j-1),j=0,即i转到上次首次匹配开头字符的下一位置,j从头开始。
int brute_match(string s, string p) {
int slen = s.size();
int plen = p.size();
int i = 0, j = 0;
while (i < slen and j < plen) {
if (s[i] == p[j]) {
++i;
++j;
} else {
i -= (j - 1);
j = 0;
}
}
if (j == plen)
return i - j;
return -1;
}

如果有一种算法能够不让i回退,只需要移动j,那么我们的算法将会大为简化。

kmp算法

在kmp算法中,引入了next数组,表示当前字符之前的子字符串中具有多大长度的相同的前缀后缀,请注意,next字符串的内容是和需要匹配的字符串p相关的

在brute-force算法的基础上改进的kmp算法如下所示:

int kmp_match(string s, string p,const vector<int> &next) {
int slen = s.size();
int plen = p.size();
int i = 0, j = 0;
while (i < slen and j < plen) {
if (j == -1 or s[i] == p[j]) {
++i;
++j;
} else {
j = next[j];
}
}
if (j == plen)
return i - j;
return -1;
}

next数组的求法

首先,我们思考一下如何计算给定字符串的最长相同前缀和后缀的长度。
  1. 设最长相同前缀后缀为str
  2. 设数组len中每一个位置和字符串s一对应,表示字符串截止到当前位置最长相同前缀后缀的长度。
  3. k表示当前位置及之前的字符串的最长相同前缀后缀的长度。s[k]表示原字符串s最长前缀后缀str之后紧跟的那个字符。
  4. 我们从第2个字符开始寻找最长前缀后缀,如果s[i]!=s[k],代表字符i无法进一步与字符j匹配,最长相同前缀后缀不可能在上一次匹配的基础之上进一步增长。如果之前能够匹配的最长相同前缀后缀长度大于0,我们不断尝试在上一次的基础之上降低标准,匹配更小长度的最长前缀后缀。
  5. 关键点在于,如何选择降低标准后的需要匹配的相同前缀后缀?这里我们将k调整为k=len[k-1],原因稍后详述
  6. 若最终经过调整之后,s[i]=s[j],这表示最长相同前缀与后缀能够在长度为k的最长相同前缀后缀的基础之上,再增长一个字符,即len[i]=++k。否则说明当前位置没有相同前缀后缀,记len[i]=0

只有长度大于1的字符串才有最长前缀与后缀,最长前缀不包括最后一个字符,最长后缀不包括第一个字符。

k=len[k-1]原因讲解

假设当前k指向字符串中f对应的位置,在e之前我们匹配到的最长相同前缀后缀为abeab,我们发现ef不能匹配,我们需要降低标准,尝试匹配更短长度的最长前缀后缀,应该匹配多长的呢?

我们发现图中下划线部分的字符串str完全相同,我们要找的长度缩减的最长相同前缀后缀长度不能超过str的长度,而且要保证这一字符串(设为str_new)满足条件:是相同的前缀后缀。即图中最前面和最后面标记的字符串str_new必须完全一样。根据对称性,这等价于在靠后的str中寻找最长前缀后缀。而且因为k比较的时候,k实际指向前面str的末尾下一位置,所以我们有:k=len[k-1]

void calculate_length(string s, vector<int> &len) {
len.resize(s.length());
len[0] = 0;
int k = 0;
for (int i = 1; i < s.length(); ++i) {
while (s[i] != s[k] and k > 0) {
k = len[k - 1];
}
if (s[i] == s[k]) {
len[i] = ++k;
} else {
len[i] = 0;
}
}
}
next数组与最长前缀后缀

next数组,表示当前字符之前的子字符串中具有多大长度的相同的前缀后缀,也就是说next 数组相当于“最大长度值” 整体向右移动一位,然后初始值赋为-1

其示例代码如下所示,因为和求最长前缀后缀的代码类似,故不再追究。

void calculate_next(string s,vector<int>&next){
next.resize(s.length());
int len = s.length();
next[0] = -1;
int k = -1;
int j = 0;
while(j<len-1){
if(k == -1 or s[j] ==s[k]){
++k;
++j;
next[j] = k;
}
else{
k = next[k];
}
}
}

kmp算法理解与记录的更多相关文章

  1. 关于KMP算法理解(快速字符串匹配)

    参考:http://www.ruanyifeng.com/blog/2013/05/Knuth%E2%80%93Morris%E2%80%93Pratt_algorithm.html 2016-08- ...

  2. [转]KMP算法理解及java实现

    这大概是我看的最好懂的KMP算法讲解了,不过我还只弄懂了大概思想,算法实现我到时候用java实现一遍 出处:知乎 https://www.zhihu.com/question/21923021/ans ...

  3. KMP算法理解(转)

    (作者matrix67) KMP算法是拿来处理字符串匹配的.换句话说,给你两个字符串,你需要回答,B串是否是A串的子串(A串是否包含B串).比如,字符串A="I'm matrix67&quo ...

  4. KMP算法理解

    1.KMP算法解决问题:对BF(Brute Force)算法优化,避免对主串进行回溯匹配(匹配不成功主串指针向后移1位,子串指针重置开始位置,两串继续匹配),效率底. 2.KMP算法原则/目的:主串不 ...

  5. 初涉KMP算法

    久仰字符串系列理论 KMP 讲解(引用自bzoj3670动物园) 某天,园长给动物们讲解KMP算法. 园长:“对于一个字符串S,它的长度为L.我们可以在O(L)的时间内,求出一个名为next的数组.有 ...

  6. 不能更通俗了!KMP算法实现解析

    我之前对于KMP算法理解的也不是很到位,如果很长时间不写KMP的话,代码就记不清了,今天刷leetcode的时候突然决定干脆把它彻底总结一下,这样即便以后忘记了也好查看.所以就有了这篇文章. 本文在于 ...

  7. KMP算法 --- 深入理解next数组

    在KMP算法中有个数组,叫做前缀数组,也有的叫next数组. 每一个子串有一个固定的next数组,它记录着字符串匹配过程中失配情况下可以向前多跳几个字符. 当然它描述的也是子串的对称程度,程度越高,值 ...

  8. KMP算法中next函数的理解

    首先要感谢http://blog.csdn.net/v_july_v/article/details/7041827以及http://blog.chinaunix.net/uid-27164517-i ...

  9. KMP算法的理解

    ---恢复内容开始--- 在看数据结构的串的讲解的时候,讲到了KMP算法——一个经典的字符串匹配的算法,具体背景自行百度之,是一个很牛的图灵奖得主和他的学生提出的. 一开始看算法的时候很困惑,但是算法 ...

随机推荐

  1. android性能优化优秀文章

    郭霖最近整理的文章: 合理管理内存 分析编码过程中如何避免过多内存占用,以及如何实现高性能的内存使用. 尽可能使用intentService; 当界面不可见时释放内存; 当内存紧张时释放内存; 避免b ...

  2. ONES 安装、配置以及初始化配置

    环境依赖 bower composer php 5.5.9+ mysql 5.6.5+ PHP和MySQL版本均为最低要求版本,安装前请先确认. 通过CLI安装 $ git clone http:// ...

  3. hdu_5711_Ingress(TSP+贪心)

    题目连接:hdu5711 这题是 HDU 女生赛最后一题,TSP+贪心,确实不好想,看了wkc巨巨的题解,然后再做的 题解传送门:Ingress #include<cstdio> #inc ...

  4. 修改document.domain的注意事项(转)

    有时候,需要修改document.domain. 典型的情形:http://a.xxx.com/A.htm 的主页面有一个<iframe src="http://b.xxx.com/B ...

  5. ubuntu下如何安装及使用 pysvn-workbench

    网上对于 pysvn-workbench 的教程几乎没有,没办法,只好去官网自学了,现在能正常上传资料了,写点东西,以免今后忘了 安装方面:在新立得中查找svn-workbench,subversio ...

  6. Java关键字transient和volatile小结(转)

    Java关键字transient和volatile小结(转) transient和volatile两个关键字一个用于对象序列化,一个用于线程同步,都是Java中比较高阶的话题,简单总结一下. tran ...

  7. arm参数传递和返回值

    参数按先后顺序传到 r0 ~ r3 如果不够4个,先传r0,以此类推 如果多于4个,入栈 返回值放在 r0中

  8. HDU 3715 Go Deeper

    二分答案 + 2-SAT判断 #include<cstdio> #include<cstring> #include<cmath> #include<vect ...

  9. Unity3d疑难问题解决

    1. 加载 c++ dll 不成功,报这个错: Failed to load 'Assets/Plugins/x86_64/myFile.dll' with error 'The operation ...

  10. MC34063中文资料及应用实例(转)

    源:http://blog.chinaunix.net/uid-26199686-id-3207838.html MC34063A(MC33063)芯片器件简介 该器件本身包含了DC/DC变换器所需要 ...