从C++strStr到字符串匹配算法
字符串的匹配先定义两个名词:模式串和文本串。我们的任务就是在文本串中找到模式串第一次出现的位置,如果找到就返回位置的下标,如果没有找到返回-1.其实这就是C++语言里面的一个函数:
extern char *strstr(char *str1, const char *str2);
对于这个函数的解释:
str1: 被查找目标
str2: 要查找对象
返回值:如果str2是str1的子串,则返回str2在str1的首次出现的地址;
如果str2不是str1的子串,则返回NULL。
例如:
char str[]="1234xyz";
char *str1=strstr(str,"34");
cout << str1 << endl;
显示的是: 34xyz
返回值是一个指针,这个指针指向文本串中第一次出现模式串的位置。
字符串查找的暴力算法
先看LeetCode上的一道题目,实现这个函数 int strStr(string haystack, string needle); ,要求返回文本串中出现模式串的下标值。
1.如果模式串为NULL,那么直接返回0.
2.如果模式串的长度大于文本串,那么一定查找不到,返回-1.
3.如果存在的话,查找的范围可以限定在文本串的0~s.size()-p.size();
所以暴力算法的代码实现:
int strStr(string haystack, string needle)
{
int i = 0;
//模式串为空
if(needle.empty())
{
return 0;
}
//文本串的大小小于模式串
if(haystack.size() < needle.size())
{
return -1;
}
//确定查找的范围
for(i = 0; i <= haystack.size()-needle.size(); ++i)
{
int j = 0;
for(j = 0; j < needle.size(); ++j)
{
if(haystack[i+j] != needle[j])
{
break;
}
}//for
if(j == needle.size())
{
return i;
}
}//for
if(i == haystack.size()-needle.size() + 1)
{
return -1;
}
}
字符串查找的KMP算法
上面的暴力算法,在查找失败以后都要进行回溯,下面再给出一个版本,明显的看到i,j的回溯:
int strStr(string haystack, string needle)
{
int sLen = haystack.size();
int pLen = needle.size(); int i = 0;
int j = 0;
while(i < sLen && j < pLen)
{
if(haystack[i] == needle[j])
{
++i;
++j;
}
else
{
i = i - j + 1;
j = 0;
}
}//while if(j == pLen)
{
return i - j;
}
else
{
return -1;
}
}
假设我们已经知道了KMP的next数组,所以每次失配以后,i不回溯,j回溯到next[j]指定的位置。也就是 j = next[j]; 。
int KmpSearch(char *s, char *p)
{
int i = 0;
int j = 0;
int sLen = strlen(s);
int pLen = strlen(p);
while(i < sLen && j < pLen)
{
//如果j = -1,或者当前字符匹配成功(即S[i] == P[j]),都令i++,j++
if(j == -1 || s[i] == p[j])
{
i++;
j++;
}
else
{
//如果j != -1,且当前字符匹配失败(即S[i] != P[j]),则令 i 不变,j = next[j]
//next[j]即为j所对应的next值
j = next[j];
}
}//while
if(j == pLen)
{
return i - j;
}
else
{
return -1;
}
}
对于上面的j=-1和next[0]=-1作下面的解释:


说完了KMP的大的框架,下面就得说一下next数组的求解过程了:
void GetNext(char *p, int *next)
{
int pLen = strlen(p);
int j = 0;
int k = -1;
next[0] = -1; while(j < pLen - 1)
{
//p[k]表示前缀,p[j]表示后缀
if(k == -1 || p[j] == p[k])
{
++k;
++j;
next[j] = k;//表示在j这个字符之前,能够构成公共前后缀的最大字符数
}
else
{
k = next[k];//回溯之前已经有过匹配的前缀
}
}
}


KMP算法的一个改进
上面的KMP算法已经能够很好的跑出结果来了,但是还可以改进,看下面的一个字符串的匹配:


改进的代码实现:
void GetNextVal(char *p, int *next)
{
int pLen = strlen(p);
int j = 0;
int k = -1;
next[0] = -1; while(j < pLen - 1)
{
//p[k]表示前缀,p[j]表示后缀
if(k == -1 || p[j] == p[k])
{
++k;
++j;
if(p[j] != p[k])
{
next[j] = k;//表示在j这个字符之前,能够构成公共前后缀的最大字符数
}
else
{
next[j] = next[k];//因为不能出现p[j] = p[next[j]],所以当出现时需要继续递归,k = next[k] = next[next[k]]
} }
else
{
k = next[k];//回溯之前已经有过匹配的前缀
}
}
}
再来看一个极端的情况:

从C++strStr到字符串匹配算法的更多相关文章
- 28. Implement strStr()(KMP字符串匹配算法)
Implement strStr(). Return the index of the first occurrence of needle in haystack, or -1 if needle ...
- KMP单模快速字符串匹配算法
KMP算法是由Knuth,Morris,Pratt共同提出的算法,专门用来解决模式串的匹配,无论目标序列和模式串是什么样子的,都可以在线性时间内完成,而且也不会发生退化,是一个非常优秀的算法,时间复杂 ...
- 4种字符串匹配算法:BS朴素 Rabin-karp(上)
字符串的匹配的算法一直都是比较基础的算法,我们本科数据结构就学过了严蔚敏的KMP算法.KMP算法应该是最高效的一种算法,但是确实稍微有点难理解.所以打算,开这个博客,一步步的介绍4种匹配的算法.也是& ...
- 字符串匹配算法之Sunday算法
字符串匹配查找算法中,最着名的两个是KMP算法(Knuth-Morris-Pratt)和BM算法(Boyer-Moore).两个算法在最坏情况下均具有线性的查找时间.但是在实用上,KMP算法并不比最简 ...
- 字符串匹配算法 - KMP
前几日在微博上看到一则微博是说面试的时候让面试者写一个很简单的字符串匹配都写不出来,于是我就自己去试了一把.结果写出来的是一个最简单粗暴的算法.这里重新学习了一下几个经典的字符串匹配算法,写篇文章以巩 ...
- Boyer-Moore 字符串匹配算法
字符串匹配问题的形式定义: 文本(Text)是一个长度为 n 的数组 T[1..n]: 模式(Pattern)是一个长度为 m 且 m≤n 的数组 P[1..m]: T 和 P 中的元素都属于有限的字 ...
- 字符串匹配算法之BF(Brute-Force)算法
BF(Brute-Force)算法 蛮力搜索,比较简单的一种字符串匹配算法,在处理简单的数据时候就可以用这种算法,完全匹配,就是速度慢啊. 基本思想 从目标串s 的第一个字符起和模式串t的第一个字符进 ...
- 【原创】通俗易懂的讲解KMP算法(字符串匹配算法)及代码实现
一.本文简介 本文的目的是简单明了的讲解KMP算法的思想及实现过程. 网上的文章的确有些杂乱,有的过浅,有的太深,希望本文对初学者是非常友好的. 其实KMP算法有一些改良版,这些是在理解KMP核心思想 ...
- 字符串匹配算法——KMP算法学习
KMP算法是用来解决字符串的匹配问题的,即在字符串S中寻找字符串P.形式定义:假设存在长度为n的字符数组S[0...n-1],长度为m的字符数组P[0...m-1],是否存在i,使得SiSi+1... ...
随机推荐
- Elasticsearch的javaAPI之percolator
Elasticsearch的javaAPI之percolator percolator同意一个在index中注冊queries,然后发送包括doc的请求,返回得到在index中注冊过的而且匹配doc的 ...
- Cannot convert '0000-00-00 00:00:00' to TIMESTAMP
在url上添加参数 zeroDateTimeBehavior=convertToNull jdbc:mysql://localhost/myDatabase?zeroDateTimeBehavior= ...
- L8_2
4.留下pid为12345的那个sh进程,杀死系统中所有其它sh进程 ps –ef|grep sh |awk ‘{if($2!=”12345”) {print “kill “$2}}’ >kil ...
- 享受一下大神的感觉, unity中用C#自己封装dll
第一次写博客,有什么不对的地方,欢迎大家指出 (1)打开vs(本人用的是vs2013)然后建一个项目,如下图所示,选择.ENT3.5的框架,或者低于3.5的,这是因为Unity里面用的框架可能在3.5 ...
- java集合类遍历删除方法测试以及使用场景记录
package test0; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java. ...
- 关于Apache+PHP+MySQL的安装
除了在安装MySQL的时候花了三天的时间,也学习了如何看错误日志,如何看配置文件等, Apache+PHP的安装都比较顺利,主要是要按照一个文档来安装,不要东拼西凑, 个人认为这篇“百度经验”写得很好 ...
- 数组转DataTable
using System; using System.Data; namespace ArrayToDataTable { class ArrayToDataTable { /// <summa ...
- 【grunt整合版】 30分钟学会使用grunt打包前端代码
grunt 是一套前端自动化工具,一个基于nodeJs的命令行工具,一般用于:① 压缩文件② 合并文件③ 简单语法检查 对于其他用法,我还不太清楚,我们这里简单介绍下grunt的压缩.合并文件,初学, ...
- make file 详
一: linux的touch命令不常用,一般在使用make的时候可能会用到,用来修改文件时间戳,或者新建一个不存在的文件. 1.命令格式: touch [选项]... 文件... 2.命令参数: -a ...
- 动态规划-Burst Balloons
Burst Balloons Given n balloons, indexed from 0 to n-1. Each balloon is painted with a number on it ...