KMP小扩展,找出子串在主串中出现的所有位置

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数组优化过的那种):

 1 vector<int> KMP(const string& S, const string& T)
 2 {
 3     vector<int> Next;
 4     Next.push_back(-1);
 5
 6     for (int i = 0, j = -1; i < T.size();) {
 7         if (j == -1 || T[i] == T[j]) {
 8             i++, j++;
 9             if (i != T.size() && T[j] == T[i]) Next.push_back(Next[j]);
10             else Next.push_back(j);
11         }
12         else j = Next[j];
13     }
14
15     vector<int> res;
16     for (int i = 0, j = 0; i < S.size() && j < (int)T.size();) {
17         if (j == -1 || S[i] == T[j]) {
18             i++, j++;
19             if (j == T.size()) {
20                 res.push_back(i - j);
21                 j = Next[j];
22             }
23         }
24         else j = Next[j];
25     }
26
27     return res;
28 }

原文地址:https://www.cnblogs.com/wulichenai/p/12252750.html

时间: 2024-10-18 01:44:45

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

计算子串在主串中的位置及其优化(KMP算法)

问题描述:设置一个起始位置,寻找主串中第一次出现子串的首位置. 算法实现: int index(string str,string substr,int pos) { int i=0,j=0; int slen,sslen; i=pos; slen=str.length(); sslen=substr.length(); while(i+sslen<slen) { while(j<sslen) { if(str[i+j]==substr[j]) j++; else break; } if(j=

poj 2406 Power Strings求子串在主串中最多叠加次数

#include<stdio.h> #define M 1000010 int n,next[M]; char s[M]; void getNext() { int i=1,j=-1; next[0]=-1; for(;s[i];i++){ while(j!=-1&&s[j+1]!=s[i])j=next[j]; if(s[j+1]==s[i])j++; next[i]=j; } n=i; } int main() { while(scanf("%s",s)

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

题目:给定一个字符串S(主串),一个字符串数组words,其中的字符串的长度相同.找到所有的子串位置,要求是words中字符串的一个连接,而且没有交叉: 举例: For example, given:s: "barfoothefoobarman"words: ["foo", "bar"] You should return the indices: [0,9]. 解题思路: 1. 采用窗口机制,假设此时每个单词的长度为wordlen; 2.  

hust 1589 找出子串

题目描述 给定一个字符串s ,求出一个子串t,满足如下性质:1.       t是s的一个前缀.2.       t是s的一个后缀.3.       t出现在s的中间(并非前缀和后缀).例如:字符串s为fixprefixsuffix,t可以是fix.字符串s为aaa,t可以是aa.输入 输入包括多组数据,每组数据为一行,每行有一个字符串s,其长度不超过10^6(一百万). 输出 每组数据输出一行,每行为一个字符串t,若不存在字符串t,则输出"Just a legend"(不包括引号).

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

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

小技巧找出一个php的cron脚本出问题的代码行

这个小技巧虽然很小,但是很有用. 我写了一个cron脚本,但是隔一天发现,这个昨天的cron脚本还一直在跑着,没有停下来,一定是里面有个程序堵住了. 但是如果我重新跑又需要很多时间.这个怎么办? 现在最需要就是要知道程序停在了那行代码上了.使用gdb: $sudo gdb -p 14973 (gdb) print (char *)executor_globals.active_op_array->filename $1 = 0x9853a34 "/home/xinhailong/test/

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

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2087 题意:求模式串在主串中出现的次数,与模式串匹配的子串之间不可重叠. 思路:用kmp算法解决,在匹配更新结果后,重新定位模式串时,不可用j = next[j],应该直接让j定位到模式串开头. code: 1 #include <cstdio> 2 #include <cstring> 3 4 const int MAXN = 1005; 5 6 char aa[MAXN]; 7 c

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

编程思想:依次求出正整数每个位数上出现1的次数,累加即可得到最后想要的结果:而每一位上出现1的个数与和它相邻的其它位数上的数字有关系(以此位置上的数为对称轴,其左边的所有数字作为其最高位,其右边的数字作为其最低位:当然若此位置已处于最低位或最高位,那么它对应的最低位或最高位置0),与它们有一个可求出1的固定的关系式(一个数各个位置上的数分离后,它们都对应着各个位置的基准,例如个位上的数对应的基准为1,以此类推即可),即若此位置上的数字为0,则在此位置上出现1的个数为最高位乘以基准:若为1,则等于

Python List index()方法-用于从列表中找出某个值第一个匹配项的索引位置

描述 index() 函数用于从列表中找出某个值第一个匹配项的索引位置. 语法 index()方法语法: list.index(obj) 参数 obj -- 查找的对象. 返回值 该方法返回查找对象的索引位置,如果没有找到对象则抛出异常. 实例 以下实例展示了 index()函数的使用方法: #!/usr/bin/python aList = [123, 'xyz', 'zara', 'abc']; print "Index for xyz : ", aList.index( 'xyz