一、问题

给定两个字符串S(原串)和(模式串)T,找出T在S中出现的位置。

二、朴素算法

当S[i] != T[j]时,把T往后移一位,回溯S的位置并重新开始比较。

  

(1) 成功匹配的部分(ABC)中,没有一样的字符

(a)

S: i A B C A B C E
T: j A B C E      

(b)

S: i A B C A B C E
T: j   A B C E    

(c)

S: i A B C A B C E
T: j     A B C E  

(d)

S: i A B C A B C E
T: j       A B C E

(2) 成功匹配的部分(ABA)中,有一样的部分(A)

(a)

S: i A B A A B A C
T: j A B A C      

(b)

S: i A B A A B A C
T: j   A B A    

(c)

S: i A B A A B A C
T: j     A B A   

(d)

S: i A B A A B A C
T: j       A B A C

三、KMP算法

基本思想:通过整理模式串T中的元素相似性,减少朴素算法中对原串S不必要的回溯。当发生失配时,回溯T到它的最长前缀的后一个位置,同时S的位置不变,再继续匹配。

前缀:包含T首字母的子串

后缀:包含T最后一个字母的子串

next数组

next[j]: 求得T[0, ..., j-1] 中最长的相同的前/后缀,next[j] 是该前缀的后一个字符所在位置。当T[j] 和S[i]不相同时,回溯T[j] 到next[j],S[i]的位置不变。

(1) next[j] =-1  if j == 0 //第一个字符的回溯位置为 -1

(2) next[ j ] = max{ k |T0...T k-1  = Tj-k-1...T j-1 } //最长的相同的前后缀,回溯时相同的部分不用再比较

(3) next[j] = 0 if 其他情况 //没有找到相同的前后缀,回溯的时候只能从第一个字符重新开始比较

计算next数组 

T中有两个相同的子串X(蓝色部分),i 和 j 是当前比较的两个位置

 (1) T[i] = T[j] = 2: next[j+1] = i+1 //T[0, ..., j] 的前缀Xi 和 后缀Xj 一样

 (2) 2 = T[i] != T[j] = 3: i = next[i] //对 i 进行回溯,重新寻找满足条件的前后缀。绿色部分,最后一个元素为 3

next数组的使用效果

(1) 成功匹配的部分(ABC)中,没有一样的字符 (省去 (b,c))

(a)

S: i A B C A B C E
T: j A B C E      
T: next[j] -1 0 0 0      

(d)

S: i A B C A B C E
T: j       A B C E
T: next[j]       -1 0 0 0

S[3] = D, T[3] = E, 不相同。j = next[3] = 0 回溯。(ABC)没有相同的部分,因此不必将 S:i 回溯再尝试匹配。

(2) 成功匹配的部分(ABA)中,有一样的部分(A) (省去 (b)) 

(a)

S: i A B A A B A C
T: j A B A C      
T: next[j] -1 0 0 1      

(c)

S: i A B A A B A C
T: j     B A C  
T: next[j]     -1 0 0 1  

(d)

S: i A B A A B A C
T: j       A B A C
T: next[j]       -1 0 0 1

S[3] = D, T[3] = C,第三个位置不匹配。j = next[3] = 1  回溯。下次比较是可以直接从S[3]和T[1]开始匹配,因为T[0] 和 T[2] 相同。

四、KMP算法源码

hihocoder】 http://hihocoder.com/problemset/problem/1015?sid=808424

 #include <iostream>
#include <string>
using namespace std; //计算next数组
void get_next(string& T, int* next)
{
int i = , j = -, Tlen = T.length();
next[] = -;
while(i < Tlen)
{
if(j == - || T[i] == T[j])
{
++i;
++j;
next[i]=(T[i] == T[j] ? next[j]:j);//使得回溯前和回溯后的元素不一样
}
else
j = next[j];
}
} //计算T在S中出现的次数
int subStrCnt(string& S, string& T)
{
int cnt = ;
int Slen = S.length(), Tlen = T.length();
int next[];
int i = , j = ;
get_next(T, next);
while(i < Slen && j < Tlen)
{
if(j == - || S[i] == T[j])
{
++i;
++j;
}
else
j = next[j];
if(j == Tlen){//T匹配完成,从T: next[j]再开始
cnt++;
j = next[j];
}
}
return cnt;
}
int main()
{
int cnt;
string S, T;
cin>>cnt;
while(cnt-- > )
{
cin>>T>>S;
cout<<subStrCnt(S, T)<<endl;
}
return ;
}

hihocoder上的一个问题:如果next是动态分配,会导致TLE。

【KMP算法】字符串匹配的更多相关文章

  1. KMP算法 字符串匹配(看猫片)

    前言 此篇笔记根据自己的理解和练习心得来解释算法,只代表个人观点,如有不足请指出(我刚学QWQ) 浅谈字符串匹配 设想一个场景,假设你是一个净化网络语言环境的管理员,每天需要翻阅大量的文章和帖子来查找 ...

  2. KMP算法——字符串匹配

    正直找工作面试巅峰时期,有幸在学校可以听到July的讲座,在时长将近三个小时的演讲中,发现对于找工作来说,算法数据结构可以算是程序员道路的一个考量吧,毕竟中国学计算机的人太多了,只能使用这些方法来淘汰 ...

  3. KMP算法---字符串匹配

    算法细节详见点击打开链接和点击打开链接 #include <stdio.h> #include <stdlib.h> #define N 7 #define M 15 void ...

  4. 算法——字符串匹配之BM算法

    前言 Boyer-Moore算法是一种基于后缀匹配的模式串匹配算法(简称BM算法),后缀匹配就是模式串从右到左開始比較,但模式串的移动依旧是从左到右的.在实践中.BM算法效率高于前面介绍的<KM ...

  5. HDU 1711(KMP)字符串匹配

    链接  HDU 1711 Number Sequence KMP 算法 我以自己理解写的,写的不对,不明白的地方海王子出来,一起共同学习: 字符串匹配 就是KMP,一般思想,用一个for循环找开头   ...

  6. KMP算法——字符匹配

     暴力匹配: 假设现在我们面临这样一个问题:有一个文本串S,和一个模式串P,现在要查找P在S中的位置,怎么查找呢? 如果用暴力匹配的思路,并假设现在文本串S匹配到 i 位置,模式串P匹配到 j 位置, ...

  7. KMP算法-字符匹配

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

  8. KMP快速字符串匹配

    KMP算法是一种改进的字符串匹配算法,由D.E.Knuth,J.H.Morris和V.R.Pratt同时发现.KMP算法的关键是利用匹配失败后的信息,从错误中吸取经验,尽量减少模式串与主串的匹配次数以 ...

  9. 算法——字符串匹配Rabin-Karp算法

    前言 Rabin-Karp字符串匹配算法和前面介绍的<朴素字符串匹配算法>类似,也是相应每一个字符进行比較.不同的是Rabin-Karp採用了把字符进行预处理,也就是对每一个字符进行相应进 ...

  10. [数据结构与算法] 字符串匹配 - BF算法

    BF(Brute Force)算法 又称暴力匹配算法,是一种朴素的模式匹配算法 给定主串 S : Bilibili 和子串 T :Bilididi 步骤: 1. 主串 S 第一位开始与子串 T 第一位 ...

随机推荐

  1. 2015.4.20 Canvas Jquery 移动端 JavaScript

    1.分享效果:弹窗Canvas渲染大图.   2.进度条中表现进度百分比的数值d%,根据进度的增长“字体颜色”为了表示清晰也随着变化 解决方法:参考Demo.   3.输入框保持查询参数,结果列表局部 ...

  2. 10W -python

    计算2 3 4 加运算符 小于30 >>> new=[''.join(('2',op,'3')) for op in ops] >>> print(new) ['2 ...

  3. Linux下,如何给PHP安装pdo_mysql扩展

    下载了一个免费开源的广告系统(openadserver),在Linux上安装时,提示要安装 pdo_mysql 扩展,先前有过编译安装 soap扩展 的经历,今天要编译安装 pdo_mysql 扩展, ...

  4. mongDB-- 3. 查询操作

    1. 准备工作 (1)启动mongo 进入mongo安装目录的bin/ 目录 , ./mongod (2)启动mongo客户端 ./mongo (3) 查看所有库 show dbs; (4) 切换到l ...

  5. sdcms留言提交

    引入这两个js <script src="{webroot}lib/validator/jquery.validator.js"></script>< ...

  6. js求时间差

    var date1=new Date();  //开始时间 alert("aa"); var date2=new Date();    //结束时间 var date3=date2 ...

  7. 分享一个Visual Studio的背景插件,让堆码更富情趣

    忘记一件重要的事情,我使用的是VS 2012版,其他更高版本应该是可以找到的,以下版本就不清楚了.有可能找不到,见谅,也不是我开发的,只是偶尔碰到,拿出来让大家知道. 上周某日,新生命群里面还是一如既 ...

  8. ACM/ICPC 之 电力网络-EK算法(POJ1459)

    按照电站发电(从源点到电站),消费者消费(从消费者到汇点)的想法构建网络,以下是EK解法 //网络流EK算法 //Time:922Ms memory:224K #include<iostream ...

  9. 定时刷新之setTimeout(只一次)和setInterval(间隔相同时间)的使用

    setTimeout和setInterval的使用 这两个方法都可以用来实现在一个固定时间段之后去执行JavaScript.不过两者各有各的应用场景. 方 法 实际上,setTimeout和setIn ...

  10. Linux服务器模型及其对应的程序流程

    一.TCP循环服务器 TCP循环服务器一次只能处理一个客户端的请求,处理完成后,才能接受下一个客户端的请求,程序流程如下: socket(......);//创建套接字 bind(......);// ...