字符串匹配算法

字符串匹配:


一、什么是最长公共子序列
   
    什么是最长公共子序列呢?举个简单的例子吧,一个数列S,若分别是两个或多个已知序列的子序列,且是所有符合条件序列中最长的,则S称为已知序列的最长公共子序列。

  举例如下,如:有两个随机数列,1 2 3 4 5 6 和 3 4 5 8 9,则它们的最长公共子序列便是:3 4 5。

  一直不明白:最长公共子串和最长公共子序列的区别。
  
   上网查了下,最长公共子串(Longest Common Substirng)和最长公共子序列(Longest Common Subsequence,LCS)的区别为:子串是串的一个连续的部分,子序列则是从不改变序列的顺序,而从序列中去掉任意的元素而获得新的序列;也就是说,子串中字符的位置必须是连续的,子序列则可以不必连续。


-:Brute Force(BF或蛮力搜索)算法:朴素字符串匹配

这是世界上最简单的算法了。
首先将匹配串和模式串左对齐,然后从左向右一个一个进行比较,如果不成功则模式串向右移动一个单位。

  BF 算法中,如果当前字符匹配成功,即 s[i+j] == T[j] ,令 j++ ,继续匹配下一个字符;如果失配,即 S[i + j] != T[j] 需要让 i++,并且j= 0 ,即每次匹配失败的情况下,模式串 T 相对于原始串 S 向右移动了一位。

普通的字符串匹配算法必须要回溯。即j=0,重新开始匹配。


KMP算法思想

KMP 算法中,如果当前字符匹配成功,即 S[i]==T[j] ,令 i++ j++ ,继续匹配下一个字符;如果 匹配失败,即S[i] != T[j] ,需要保持 i 不变,并且让 j = next[j] ,这里 next[j] <=j -1 ,即模式串 T 相对于原始串 S 向右移动了至少 1 ( 移动的实际位数 j - next[j]  >=1)

next数组的含义

重点来了。下面解释一下next数组的含义,这个也是KMP算法中比较不好理解的一点。

  令原始串为: S[i],其中0<=i<=n;模式串为: T[j],其中0<=j<=m

  假设目前匹配到如下位置

               S0,S1,S2,...,Si-j,Si-j+1...............,Si-1,Si, Si+1,....,Sn

                                   T0,T1,.....................,Tj-1,Tj, ..........

  ST的绿色部分匹配成功,恰好到SiTj的时候失配,如果要保持i不变,同时达到让模式串T相对于原始串S右移的话,可以更新j的值,让Si和新的Tj进行匹配,假设新的jnext[j]表示,即让Sinext[j]匹配,显然新的j值要小于之前的j值,模式串才会是右移的效果,也就是说应该有next[j] <= j -1。那新的j值也就是next[j]应该是多少呢?我们观察如下的匹配:

      1)如果模式串右移1位(从简单的思考起,移动一位会怎么样),即next[j] = j - 1, 即让蓝色的SiTj-1匹配(注:省略号为未匹配部分)

               S0,S1,S2,...,Si-j,Si-j+1...............,Si-1,Si, Si+1,....,Sn

                                   T0,T1,.....................,Tj-1,Tj, .......... (T的划线部分和S划线部分相等【1】)

                                        T0,T1,.................Tj-2,Tj-1,....... (移动后的T的划线部分和S的划线部分相等【2】)

        根据【1】【2】可以知道当next[j] =j -1,即模式串右移一位的时候,有T[0 ~ j-2] == T[1 ~ j-1],而这两部分恰好是字符串T[0 ~j-1]的前缀和后缀,也就是说next[j]的值取决于模式串Tj前面部分的前缀和后缀相等部分的长度(好好揣摩这两个关键字概念:前缀、后缀,或者再想想,我的上一篇文章,从Trie树谈到后缀树中,后缀树的概念)。

      2)如果模式串右移2位,即next[j] = j - 2, 即让蓝色的SiTj-2匹配    

               S0,S1,...,Si-j,Si-j+1,Si-j+2...............,Si-1,Si, Si+1,....,Sn

                                   T0,T1,T2,.....................,Tj-1,Tj, ..........(T的划线部分和S划线部分相等【3】)

                                              T0,T1,...............,Tj-3,Tj-2,.........(移动后的T的划线部分和S的划线部分相等【4】)

        同样根据【3】【4】可以知道当next[j] =j -2,即模式串右移两位的时候,有T[0 ~ j-3] == T[2 ~ j-1]。而这两部分也恰好是字符串T[0 ~j-1]的前缀和后缀,也就是说next[j]的值取决于模式串Tj前面部分的前缀和后缀相等部分的长度

     3)依次类推,可以得到如下结论:当发生失配的情况下,j的新值next[j]取决于模式串中T[0 ~ j-1]中前缀和后缀相等部分的长度, 并且next[j]恰好等于这个最大长度

    S:  ababcababa

    P:  ababa

 next [j]表示j之前的最长前缀匹配最长后缀的长度是多少。

 P      a    b   a    b   a

 j      0    1   2    3   4

 next    -1   0   0    1   2

Horspool字符串匹配算法


Horspool和Kmp算法有点相识,都是采用空间换时间的想法,从而达到算法运算速率的提高,运算效率也都是θ(n),在最佳情况下,它的时间复杂度是O(n/m),

Horspool算法
这个算法是由R.Nigel Horspool在1980年提出的。其滑动思想非常简单,就是从后往前匹配模式串,若在某一位失去匹配,此位对应的文本串字符为c,那就将模式串向右滑动,使模式
串之前最近的c对准这一位,再从新从后往前检查。那如果之前找不到c怎么办?那好极了,直接将整个模式串滑过这一位。

1)A G A T A C G A T A T A T A C

    A T A T A

   在主串的G处失配,此时模式串向右移动的距离是d[A]=2

2)A G A T A C G A T A T A T A C

          A T A T A

  又在主串G处失配,此时模式串向右移动的距离是d[G]=5

3)A G A T A C G A T A T A T A C

                        A T A T A

 此处发现了一个匹配,我们可以继续往下找,此时模式串向右移动的距离是d[A]=2

4)A G A T A C G A T A T A T A C

                              A T A T A

 呃。。。又发现了一个匹配,再继续往下找吧,此时模式串向右移动的距离依然是d[A]=2

5)A G A T A C G A T A T A T A C

                                   A T A T A

移动后,主串指针pos>n-m了,搜索过程结束。


BM算法   

而BM算法在移动模式串的时候是从左到右,而进行比较的时候是从右到左的,基本框架是:

  1. j = 0;   
  2. while (j <= strlen(主串) - strlen(模式串)) {   
  3.    for (i = strlen(模式串) - 1; i >= 0 && 模式串[i] ==主串[i + j]; --i)   
  4.       if (i < 0)  
  5.           match;  
  6.        else   
  7.           ++j;  
  8. }  

显然BM算法并不是上面那个样子,BM算法的精华就在于++j


    BM算法在移动模式串的时候是从左到右,而进行比较的时候是从右到左的,

BM算法在进行匹配时,包含两个并行的算法,坏字符算法和好后缀算法,这两种算法的目的就是为了让模式串每次向右移动尽可能大的距离(j+=s,s尽可能的大)


几个定义:

例主串和模式串如下:

主串  :  mahtavaatalomaisema omalomailuun

模式串: maisemaomaloma

好后缀:模式串中的aloma为“好后缀”。

坏字符:主串中的“t”为坏字符。


1、好后缀算法

好后缀确定的偏移量分为如下两种情况:

Case1:模式传中有字串和好后缀完全比配,则将最靠右的那个字串移动到好后缀的位置,继续进行匹配。(shift即为偏移量)(m-1-address(u))

       Case2:如果不存在和好后缀完全匹配,则在好后缀中找到具有如下特征的最长子串,使得P[m-s,…,m]=P[0,…,s]。最长前后缀。

好后缀算法

如果程序匹配了一个好后缀, 并且在模式中还有另外一个相同的后缀, 那

把下一个后缀移动到当前后缀位置。好后缀算法有两种情况:

Case1:模式串中有子串和好后缀安全匹配,则将最靠右的那个子串移动到好后缀的位置。继续进行匹配。

wps_clip_image-979

Case2:如果不存在和好后缀完全匹配的子串,则在好后缀中找到具有如下特征的最长子串,使得P[m-s…m]=P[0…s]。

wps_clip_image-1152


3、坏字符算法

当出现一个坏字符时, BM算法向右移动模式串, 让模式串中最靠右的对应字符与坏字符相对,然后继续匹配。坏字符算法也有两种情况。

Case1:模式串中有对应的坏字符时,见图。
wps_clip_image-1349

Case2:模式串中不存在坏字符。见图。

wps_clip_image-1472



移动规则

BM算法的移动规则是:

将概述中的++j,换成j+=MAX(shift(好后缀),shift(坏字符)),即

BM算法是每次向右移动模式串的距离是,按照好后缀算法和坏字符算法计算得到的最大值。



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值