关于字符串精确匹配

Posted ykzou

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了关于字符串精确匹配相关的知识,希望对你有一定的参考价值。

0 引子

嗯,开始之前先介绍几个概念:
目标串:也就是主串,待匹配的串。
模式串:去匹配的串。
子串:原串中的某一连续片段。
前缀:原串前面连续部分组成。
后缀:原串尾部连续部分组成
其实,不用被这些术语搞晕,更不必记忆,转化为自己的东西,理解了就好。

抛个问题先:
现在有两个字符串,其中一个是模式串abcabcacab,另一个是目标串babcbabcabcaabcabcabcacabc,用什么方法能快速判断目标串中是否包含模式串。

学习是个循序渐进的过程,学习算法尤其如此,所以我们由经典算法开始,一步一步深入。
经典的算法思想就是挨个匹配,失配了就整体对齐到下一位继续匹配。
step1:

10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
b a b c b a b c a b c a b c a a b c a b c a c a b c
a b c a b c a c a b                                
N                                                  

第一位不匹配。

step2:

10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
b a b c b a b c a b c a b c a a b c a b c a c a b c
  a b c a b c a c a b                              
        N                                          

第四位不匹配。

太多就不展开了,这样依次下去,最后肯定可以得出正确的结果。

哎,太容易想到的往往效率不高,经过上面的介绍,大家应该对这个问题有了自己的认识,我一直觉得,想要解决问题一定要对问题本身有深刻的认识,这也是我一直跟队友强调的。如果没有知其然,知其所以然的态度,建议不用继续看下去,毕竟下面要说的单模式匹配KMP算法和BM算法,都出来这么多年了,很多库都有封装,对于很多人来说真的是会用就行。

1 KMP算法

有时刷一些字符串相关的题时,经常会用到KMP算法,其实时间长了,自己也有点忘,就直接依靠以前的模板了,现在网络方便了,自己却变懒惰了,扪心自问:你能给一个完全没这方面基础的人,讲清楚什么是KMP算法吗?

KMP算法是三个人共同提出的,K,M,P分别是这三个人名字的首字母。KMP算法的主要思想是,利用模式串自身的信息,得到next表,next表主要用于失配时的跳跃。

10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
b a b c b a b c a b c a b c a a b c a b c a c a b c
          a b c a b c a c a b                      
          Y Y Y Y Y Y Y N                          

上面是匹配过程中的一个状态,为了叙述方便,现在将模式串称P,目标串称为T,现在T[7] != P[12],观察下我们发现1. T[3~6] = P[8~11] 2. T[0~3] = T[3~6],发现这两点应该没什么难度。代换下,进一步发现T[0~3] = P[8~11],这样我们就不用像经典的算法那样,一次只跳一步,现在我们可以直接跳四步,直接去匹配第五位。

10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
b a b c b a b c a b c a b c a a b c a b c a c a b c
                a b c a b c a c a b               a
                         ?                          

根据上面的结论,前四位匹配肯定成功,直接从第五位开始比较。

通过上面的分析,我们尝到了KMP思想的甜头,一次跳跃了四位,避免了很多无效的比较。仔细的观察下,上面的做法其实只用到了模式串的特性,和目标串并没什么联系,如果我们提前得到一张模式串的next表,那么失配时,不是可以直接去查表然后计算如何跳跃吗?嗯,对!关键是怎么得到这张表。

得到next表前,我们先要得到这样一张表,前缀自包含的长度,还是举个例子吧,对于上面的模式串T,我们求T[5]的前缀自包含长度,abcabcacab主要就是求T[0~4]中T[0~x] = T[4-x~4]中x的值,显然这里的x=1,那么T[5]前缀自包含的长度就是2,移动的步数 = 已匹配的长度 - 前缀自包含长度 = 5 - 2 = 3。也就是说在T[5]处失配时,直接向前跳3步,因为已经匹配的长度等于5,即T[0~4],前缀自包含长度等于2。

KMP的精髓也就next表,而next表的核心是前缀自包含长度,现在大家应该比较清晰了,梳理下思路就可以自己写出来了。

比较晚了,下面的BM算法,AC算法,WM算法先欠着。 0. 0

2 BM算法

这货的效率平均比KMP高3~4倍(要知道,KMP已经是O(n)的!),平时编辑器里的ctrl+F,grep之类的都是使用的这个算法。但是这个算法的核心和KMP没什么大的区别,只是换了种方式,再加上了一些自己的规则。

3 AC算法

多模式KMP算法。嗯,姑且这样理解吧!

4 WM算法

多模式BM算法。嗯,姑且这样理解吧!

未完待续。。。

以上是关于关于字符串精确匹配的主要内容,如果未能解决你的问题,请参考以下文章

Linux shellgrep命令精确匹配字符串查找

Apache 2.4 精确重定向匹配字符串

在 C++ 中使用 std::regex 匹配精确的子字符串

用于多个参数精确匹配的 C# 字符串方法 [重复]

Pandas str.contains 用于部分字符串的精确匹配

pyspark字符串匹配多个精确单词正则表达式的有效方法