KMP算法能够高效地匹配字符串,找出子串(T串)在主串(S串)中出现的首个位置的原算法网上已经有很多优秀的博文进行详细讲解,这里就不多赘述。

这篇博文主要是对KMP原算法稍作改动,使其能够在主串中把所有匹配的主串找出来。


找出首个匹配的算法好弄,next数组求出来后直接用来匹配,直到出现完全匹配的情况的时候就停止搜索把答案扔出来就行,但是想把所有T串找出来的话就得完全把S串搜完, 就算已经在S串中找到一个T串后也是不能马上停止搜索的。

难点就在已经完全匹配了一个T串以后怎么继续进行下一个匹配。

完全匹配T串后,我们需要将S串的i指针往下挪一位,那么容易知道前面的字符串都已经是匹配过的了,根据KMP算法的思想,我们需要将T串的j指针进行回溯就能继续匹配,问题就在于这个j指针应该回溯到哪里才合适。

这里给出一个例子

主串(S串) ababaab

子串(T串) aba

这里的下标就从0开始吧,我们看到T串在S串中出现的位置是0和2,现在我们先把第一次匹配的情况画出来,这里next数组也顺便给出来了,这是没优化过的next数组

然后根据原KMP算法,i++, j++

按照原算法,在下个循环当中应该已经退出循环了,但是在这里我们当然不能这么做,所以我们应该将j回溯,不过这问题来了,之前我们进行回溯是根据next数组来将j进行回溯的,在上图的情况下,我们可以看到j所指的位置并没有对应的next值,那怎么办?

先不讨论算法,就单纯观察我们知道上图中的j应该是回溯到了b(T串1位置)那里,因为T串末尾的a跟首部的a相同,也就是这部分的后缀分前缀是相同的,根据KMP算法思想,这部分匹配过,就应该回溯到相同前缀的后一位,这就回溯到了b

到这里我们就已经是发现了,其实这种回溯就是在把next数组多往后求一位,虽然传统的KMP算法在求next数组时只是求出跟T串等长长度而已,但其实多往后求一位也是可以的,我们回到求T串求到最后一位时的情景:

指针的情况在如上图所示时,按传统算法本该退出循环的,但是理论上确实还是可以再往后求一位的,这里又有T[i] == T[j],所以i++, j++,然后next[i] = j,这样,我们就又额外得到了一位next数组的值

这个最后一位的next值意义是一样的,我们可以把这个字符串看成是长度为4的字符串,然后这个T[3]字符非常诡异,任何字符都不与它相等,也就是说,这个位置是必定匹配失败的,但是由于前3位字符都匹配成功了,所以这个回溯依然是合理的。

这样一来,S串中找出多个T串的算法就好弄了,这个T串我们看成是原先T串上多延长了一位,但是这个最后一位怎么匹配也不会匹配成功,所以在这个匹配算法中最多只会匹配到原T串长度,然后匹配到T串+1长度时就会回溯j指针,通过这种方法,我们就可以找出所有的T串出现位置了。


附代码(next数组优化过的那种):

 vector<int> KMP(const string& S, const string& T)
{
vector<int> Next;
Next.push_back(-); for (int i = , j = -; i < T.size();) {
if (j == - || T[i] == T[j]) {
i++, j++;
if (i != T.size() && T[j] == T[i]) Next.push_back(Next[j]);
else Next.push_back(j);
}
else j = Next[j];
} vector<int> res;
for (int i = , j = ; i < S.size() && j < (int)T.size();) {
if (j == - || S[i] == T[j]) {
i++, j++;
if (j == T.size()) {
res.push_back(i - j);
j = Next[j];
}
}
else j = Next[j];
} return res;
}

KMP小扩展,找出子串在主串中出现的所有位置的更多相关文章

  1. search for a range(找出一个数在数组中开始和结束位置)

    Given an array of integers sorted in ascending order, find the starting and ending position of a giv ...

  2. KMP模版 && KMP求子串在主串出现的次数模版

    求取出现的次数 :  #include<bits/stdc++.h> ; char mo[maxn], str[maxn];///mo为模式串.str为主串 int next[maxn]; ...

  3. Leetcode30--->Substring with Concatenation of All Words(主串中找出连接给定所有单词的子串的位置)

    题目:给定一个字符串S(主串),一个字符串数组words,其中的字符串的长度相同.找到所有的子串位置,要求是words中字符串的一个连接: 举例: For example, given:s: &quo ...

  4. 小易邀请你玩一个数字游戏,小易给你一系列的整数。你们俩使用这些整数玩游戏。每次小易会任意说一个数字出来,然后你需要从这一系列数字中选取一部分出来让它们的和等于小易所说的数字。 例如: 如果{2,1,2,7}是你有的一系列数,小易说的数字是11.你可以得到方案2+2+7 = 11.如果顽皮的小易想坑你,他说的数字是6,那么你没有办法拼凑出和为6 现在小易给你n个数,让你找出无法从n个数中选取部分求和

    小易邀请你玩一个数字游戏,小易给你一系列的整数.你们俩使用这些整数玩游戏.每次小易会任意说一个数字出来,然后你需要从这一系列数字中选取一部分出来让它们的和等于小易所说的数字. 例如: 如果{2,1,2 ...

  5. HDU 2087 剪花布条(模式串在主串中出现的次数主串中子串不可重叠)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2087 题意:求模式串在主串中出现的次数,与模式串匹配的子串之间不可重叠. 思路:用kmp算法解决,在匹 ...

  6. 《找出1到正整数N中出现1的次数》

    <找出1到正整数N中出现1的次数> 编程思想:依次求出正整数每个位数上出现1的次数,累加即可得到最后想要的结果:而每一位上出现1的个数与和它相邻的其它位数上的数字有关系(以此位置上的数为对 ...

  7. C语言:对传入sp的字符进行统计,三组两个相连字母“ea”"ou""iu"出现的次数,并将统计结果存入ct所指的数组中。-在数组中找出最小值,并与第一个元素交换位置。

    //对传入sp的字符进行统计,三组两个相连字母“ea”"ou""iu"出现的次数,并将统计结果存入ct所指的数组中. #include <stdio.h& ...

  8. java基础知识回顾之---java String final类普通方法的应用之“子串在整串中出现的次数”

    /* * 2 一个子串在整串中出现的次数. * "loveerlovetyloveuiloveoplove" * 思路: * 1,要找的子串是否存在,如果存在获取其出现的位置.这个 ...

  9. 找出N个无序数中第K大的数

    使用类似快速排序,执行一次快速排序后,每次只选择一部分继续执行快速排序,直到找到第K个大元素为止,此时这个元素在数组位置后面的元素即所求 时间复杂度: 1.若随机选取枢纽,线性期望时间O(N) 2.若 ...

随机推荐

  1. window10家庭版解决IIS中万维网服务的安全性中无Windows身份验证

    首先在左下角输入cmd搜索->命令提示符->以管理员身份运行->然后复制下面一段命令: dism /online /norestart /add-package:%SystemRoo ...

  2. [IOI2018] werewolf 狼人 [kruskal重构树+主席树]

    题意: 当你是人形的时候你只能走 \([L,N-1]\) 的编号的点(即大于等于L的点) 当你是狼形的时候你只能走 \([1,R]\) 的编号的点(即小于等于R的点) 然后问题转化成人形和狼形能到的点 ...

  3. 广度优先搜索BFS---求出矩阵中“块”的个数

    题目: 给出一个 m x n 的矩阵,矩阵中的元素为0或1.如果矩阵中有若干个 1是相邻的,那么称这些1构成了一个“块”.求给定的矩阵中“块”的个数. 0 1 1 1 0 0 1 0 0 1 0 0 ...

  4. Jstree在加载时和加载完成的回调方法-sunziren

    1.有时候在使用jstree的时候我们想在它加载完成后立刻执行某个方法,于是我们可以用下面这个jstree自带的回调: .on('ready.jstree', function(event, obj) ...

  5. springboot打成war包并携带第三方jar包

    1.修改打包方式为war     <packaging>war</packaging> 2.添加第三方依赖的jar到pom     我的第三方jar包在resoueces目录下 ...

  6. bootstrap234的ie兼容选择

    如果你需要兼容IE8甚至是IE7和IE6,那么只能选择Bootstrap2,虽然它自身在IE6的效果也并不完美.如果需要兼容IE678的话用2.如果需要高版本的浏览器,并且移动端优先的话,那么用boo ...

  7. MySQL char与varchar 的区别

    一.差异 1.占用存储空间上 char 初始化时占固定空间,varchar依据插入内容大小使用空间. 2.char最大字符长度255个(约0.1KB),varchar则是65535(约192KB). ...

  8. 多重集组合数 简单dp

    #include <cstdio> #include <iostream> using namespace std; +; +; +; ; int n,m,M; int a[m ...

  9. Python 测试代码 初学者笔记

    单元测试 每完成一个单元测试,Python都会打印一个字符: 测试通过打印一个句点:测试引发错误打印E:测试导致断言失败打印F 模块unittest import unittest from name ...

  10. linux vi编辑器光标跳到文件末尾

    1.:0,:1,gg都可以到第一行2.shift+g到末行3.或者:$=检查总行数比如24,:24到第24行