php随机匹配算法,字符串匹配的KMP算法+PHP实现
1. 前言
看了阮一峰的字符串匹配的KMP算法,写得很好,推荐看看。
不过我想自己写个例子描述一下这个算法,顺便写个PHP实现,于是有了这篇博文。
2. 概述 [来自维基百科]
字符串搜索算法
字符串搜索算法(String searching algorithms)又称字符串比对算法(string matching algorithms)是一种搜索算法,是字符串算法中的一类,用以试图在一长字符串或文章中,找出其是否包含某一个或多个字符串,以及其位置。
部分算法比较 [克努斯-莫里斯-普拉特算法即KMP算法]
令 m 为模式的长度, n 为要搜索的字符串长度, k为字母表长度。
image.png
3. 算法解读
3.1 例子
给定字符串:
RXYZAHXFXYZAXYZAXYZ
需要搜索的字符串是:
XYZAXY
朴素匹配的步骤很简单,先对比两个字符串的第一个字母,如果不一样,对比给定字符串的下一个字母,如果一样,那么对比两个字符串的第二个字母,以此类推。这种算法的缺点是效率低,因为做了很多无用工,比如,我在匹对 RXYZAHXFXYZAXYZAXYZ和XYZAXY字符串的时候,我在匹对给定字符串的XYZAH和要搜索的字符串XYZAXY这一步,前四个字母的已经匹对成功的,这四个字母是
XYZA
这个字符串的前缀和后缀
前缀:
[X,XY,XYZ]
后缀:
[YZA,ZA,A]
没有共同的元素,这表示在这个长度内,不会有能和要搜索字符串的前缀匹配的部分,那么就可以直接跳过这一部分字符串的对比。这就是KMP算法的核心。所以,我们首先要对要搜索的字符串生成一个匹配表。
3.2 部分匹配表
如何给字符串生成一个匹配表?
XYZAXY
- X 前缀和后缀都是空,所以共有元素的长度0;
- XY 前缀[X],后缀[Y], 共有元素的长度0;
- XYZ 前缀[X,XY],后缀[Y,YZ],共有元素的长度0;
- XYZA 前缀[X,XY,XYZ],后缀[YZA,ZA,A],共有元素的长度0;
- XYZAX 前缀[X,XY,XYZ,XYZA],后缀[YZAX,ZAX,AX,X],共有元素X的长度1;
- XYZAXY 前缀[X,XY,XYZ,XYZA,XYZAX],后缀[YZAXY,ZAXY,AXY,XY,Y],共有元素XY的长度2;
因此生成的匹配表:
image.png
3.3 匹配
好了,现在来看一下怎么使用上一步骤的表。
首先,移位的计算公式:
移动位数 = 已匹配的字符数 - 对应的部分匹配值
下面看具体步骤:
步骤1
image.png
匹配第一个字符,不一致,则将要搜索的字符串右移一位,直至到匹配第1位字母。
步骤2
image.png
从第一个字符串的第一个X开始,已匹配是4位,查表最后一位匹配的字母对应的匹配值是0,所以右移4位。
步骤3
image.png
移位后发现不匹配,又要继续右移一位,直至匹配第1位字母。
步骤4
image.png
发现匹配了X之后,下一位右不匹配了,查表得到X对应的匹配值是0,1-0=1,再右移一位。
步骤5
image.png
继续匹配,666,发现完全匹配,所以呀,就是找到了一个出现的地方。这时候,已匹配是6个,查表部分匹配值2,移动位数6-2=4.
步骤6
image.png
又继续匹配,发现又发现一个。这时候移位位数是4,给定字符串已经没了,所以匹配终止。
3.4 结论
要搜索的字符串在给定字符串出现的次数是2次,如图:
image.png
4. 伪代码[来自《算法导论》]
预处理,生成部分匹配表
//注意:伪代码下标都是从1开始,这是《算法导论》约定俗成的
COMPUTE-PREFIX-FUNCTION(P)
m = P.length
let π[1...m] be a new array
π[1] = 0
k = 0
for q = 2 to m
while k > 0 and P[k +1] != P[q] //这一步很巧妙,但是比较难理解,下面解释
k = π[k]
if P[k + 1] == P[q] //对比上一个部分匹配值的下一个字母是否与当前字母一致
k = k + 1
π[q] = k //保存q对于的部分匹配值
return π
下面解释上面伪代码COMPUTE-PREFIX-FUNCTION的关键步骤
while k > 0 and P[k +1] != P[q]
k = π[k]
举个比较好理解的例子:
P = XYXYXZT
假设当前运行到Z,则之前生成的 π为
π[1~5] = [0,0,1,2,3];
此时 k = 3, q = 6, P[4] != P[6] 符合while循环的条件,进入循环。
1.P[4]= P[6]:
首先,在这里为什么要比较P[4] 和 P[6]?这是因为Z前面X对于的π[5] = 3,
这说明P[1~3] = P[3~5],所以一旦P[4]= P[6],则立马可以
进行下一步的k=k+1(这时if的判断结果肯定的true);
2.如果P[4] != P[6]:
这时候我们把注意力放在P[1~3],这时候我们求的是P[1~3]前缀和P[4~6]的后缀的公共元素。
这时候我们获取P[3]的部分匹配值,π[3]=1,说明P[1] = P[3]。由上面的P[1~3] = P[3~5]知道,
P[1] = P[3] = P[5],那么我们只需比较P[2] = P[6],无需从头匹配一遍P[1~3]=P[4~6],假设相等,
这时候就有P[1~2] = P[5~6],对应的部分匹配值+1,但是,很不幸,这时候依然不等,所以继续循环。
3. 结论:
k = π[k]其实是利用了部分匹配值提供的信息减少比较次数。
匹配
KMP-MATCHER(T, P)
n = T.length
m = P.length
π = COMPUTE-PREFIX-FUNCTION(P)
q = 0
for i = 1 to n
while q > 0 and P[q + 1] != T[i] //这一步和上面的那一步本质上意义是相同的
q = π[q]
if P[q + 1] == T[i]
q = q + 1
if q == m
print "Pattern occurs with shift" i - m
q = π[q]
运行时间分析
运行摊还分析的聚合方法进行分析,过程COMPUTE-PREFIX-FUNCTION的运行时间为Θ(m)。唯一微妙的部分是while循环总共执行时间是O(m)。下面将说明它至多进行了m-1次迭代。我们从观察k的值开始:
1)初始值0,并且增加k的唯一方法是
if P[k + 1] == P[q]
k = k + 1
这个在for循环每次迭代至多执行一次,因此,k总共增加m-1次;
2) 在进行for循环时,k
3)k永远不可能为负值。
综上,k的递减来自于while循环,它由k在所有for循环迭代中的增长所限定,k总共下降m-1。因此,while循环最多迭代m-1次,并且COMPUTE-PREFIX-FUNCTION的运行时间为Θ(m)。
同样的方法分析可知KMP-MATCHER的时间复杂度是Θ(n)。
5. PHP实现
class Kmp {
/**
* 生成部分匹配表
* @param string $p
* @return array
*/
public function ComputePrefix($p)
{
$m = strlen($p);
$table = [];
$table[0] = 0;
$k = 0;
for($q = 1; $q < $m; $q++)
{
while ($k > 0 && $p[$k] != $p[$q])
$k = $table[$k];
if($p[$k] == $p[$q])
$k = $k + 1;
$table[$q] = $k;
}
return $table;
}
/**
* 匹配
* @param string $str
* @param string $p
*/
public function Matcher($T, $p)
{
$n = strlen($T);
$m = strlen($p);
$table = $this->ComputePrefix($p);
$q = 0;
$match = [];
for($i = 0; $i < $n; $i++)
{
while ($q > 0 && $p[$q] != $T[$i])
$q = $table[$q];
if($p[$q] == $T[$i])
$q = $q + 1;
if($q == $m) {
$match[] = ['begin' => $i - $m + 1, 'end' => $i];
$q = $table[$q - 1];
}
}
return $match;
}
}
$kmp = new Kmp();
$match = $kmp->Matcher('RXYZAHXFXYZAXYZAXYZ', 'XYZAXY');
运行结果:
image.png
即匹配结果有两个,分别是
$p[8] ~ $p[13]
和
$p[12] ~ $p[17]
php随机匹配算法,字符串匹配的KMP算法+PHP实现相关推荐
- [算法系列之二十六]字符串匹配之KMP算法
一 简介 KMP算法是一种改进的字符串匹配算法,由D.E.Knuth与V.R.Pratt和J.H.Morris同时发现,因此人们称它为克努特-莫里斯-普拉特操作(简称KMP算法).KMP算法的关键是利 ...
- 字符串匹配(KMP 算法 含代码)
主要是针对字符串的匹配算法进行解说 有关字符串的基本知识 传统的串匹配法 模式匹配的一种改进算法KMP算法 网上一比較易懂的解说 小样例 1计算next 2计算nextval 代码 有关字符串的基本知 ...
- 算法串匹配C++实现字符串匹配的KMP算法
新手发帖,很多方面都是刚入门,有错误的地方请大家见谅,迎欢批评指正 之前看<算法导论>符字串匹配分部讲到KMP算法,被弄得云里雾里.天今看到阮一峰写了一篇博客<符字串匹配的KMP算法 ...
- Java 字符串匹配的KMP算法
package Four;import java.util.Scanner;/**** 字符型的kmp算法* @author bai* 描述:* 给你一个文本串,再给你一个模式串,* 文本串中有多少个 ...
- 三十五、字符串匹配问题--KMP算法
一.暴力匹配算法实现字符串匹配 如果用暴力匹配的思路,并假设现在 str1 匹配到 i 位置,子串 str2 匹配到 j 位置,则有: 如果当前字符匹配成功(即 str1[i] == str2[j]) ...
- 字符串匹配之KMP算法详解
kmp算法又称"看毛片"算法,是一个效率非常高的字符串匹配算法.不过由于其难以理解,所以在很长的一段时间内一直没有搞懂.虽然网上有很多资料,但是鲜见好的博客能简单明了地将其讲清楚. ...
- 字符串匹配的KMP算法(转)
转自:http://www.ruanyifeng.com/blog/2013/05/Knuth%E2%80%93Morris%E2%80%93Pratt_algorithm.html 字符串匹配是计算 ...
- 字符串匹配问题 ----- KMP算法
题意: 任意给定一段字符串str("123abc123abc00abc") 再输入一个关键字key("abc") 要求返回str中包含key的所有子串的头下标 ...
- 【转载】字符串匹配的KMP算法
转自:http://www.ruanyifeng.com/blog/2013/05/Knuth%E2%80%93Morris%E2%80%93Pratt_algorithm.html 其他参考:htt ...
最新文章
- 实战:使用 Mask-RCNN 的停车位检测
- exit命令的返回值
- 网页设计精粹:网页中那些迷人的按钮设计
- Verilog功能模块——符号位扩展
- python爬虫--如何爬取翻页url不变的网站
- CSS样式表的规划与组织
- 1073. Pearls
- 什么是激光雷达技术?
- 基于逻辑回归算法模型搭建思路
- JavaScript调用Linux系统命令
- 《杂记》- 之- 使用windows终端命令查看文件的MD5
- 商淘软件S2B2C供应链系统 支持多种电商模式
- Filter vs Listener
- java excelhandle oschina,基于alibab的easyexcel进行excel表的导出(可自定义handler去设计excel格式)...
- 一本通1325:【例7.4】 循环比赛日程表
- 项目系统设计和数据库设计(追光的人)
- 基于安卓的高清语音技术亮相中国国际通信展览会
- win10 屏幕保护时间到了不触发_你真的了解Win10么?网友教你玩转Win10!
- 上海的211大学中计算机,上海有哪些211大学
- 大数据处理算法--Bloom Filter布隆过滤