zoukankan      html  css  js  c++  java
  • 单模式匹配匹配算法

    背景:字符串匹配问题是最典型的模式匹配问题,是许多文本编辑、数据检索和符号操作问题的重要组成部分,同时在信号处理领域,计算生物学中的序列分析和基因比对,以及OCR(Optical Character Recognition)纠错等领域也都有重要应用。字符串匹配问题的求解算反总体上包括:动态规划、自动机方法、位并行运算、过滤算法。

    问题概述:字符串的匹配问题一直被视为计算机科学的基本问题之一。早期的研究多集中于精确匹配领域,1970年,S.S.Cook从理论上证明单模式匹配问题可以在O(m+n)时间内解决,为字符串匹配算法的发展奠定了理论基础;D.E.Knuth、V.R.Pratt和T.H.Moms仿照Cook的证明构造了KMP算法;R.S.Boyer和J.S.Moore设计了BM算法;以及利用位并行方法设计的Shift-And和Shift-Or算法。以及基于子串搜索的Backward Dawg Matching(BDM)、Backward Nondeterministic Dawg Matching(BNDM)利用自动机的算法和利用Factor Oracle的Backward Oracle Matching(BOM)算法。

    所谓单模式匹配问题,就是在一个大文本(text)T=t1t2...tn 中找到某个特定的模式串(pattern)P=p1p2p3...pm出现的位置,文本长度为n,模式串长度为m,其中T和P都是在有限字母表(alphabet)∑的字符序列,大小为δ。设x、y、z为模式串,则x为xy的前缀,x为yx的后缀,x为yxz的一个因子或子串。如果存在两个字u和v使得w=uz=zv,则z是w的边界(border),是w的前缀也是它的后缀。注意在这种情况下|u|=|v|,并且它们是w的一个period。

    单模式字符串匹配算法

    单模式匹配算法按搜索方式可以分为基于前缀搜索,基于后缀搜索和基于子串搜索三种方式。

    基于前缀搜索的代表算法主要有KMP,Shift-And和Shift-Or,从前向后判断字符与pattern的匹配情况,根据已匹配成功的信息确定最大的pattern后移位数。基于后缀搜索的代表算法主要是BM以及对BM改进的Horspool和Sunday算法,主要的思想是在匹配时从后向前,先匹配pattern的最后一个字符,这样匹配失败后很容易扩大后移位数。基于子串搜索的算法主要是利用自动机的思想,主要包括BDM,BNDM和BOM。

    BF算法:BF算法是出现最早的一种算法,从左到右移动pattern依次比较,一次移动一个字符距离。时间复杂度为O(mn),辅助空间为常数。

    KMP算法:是一种对BF算法的改进,依然是从左向右移动,利用已匹配成功的信息,即前缀模式(模式中不同部分存在的相同子串)可以使模式向后推进若干字符位置,而不只是一个字符,避免了重复比较,同时也实现了文本指针的无回溯。

    预处理阶段时间、空间复杂度均为O(m),搜索阶段时间复杂度为O(n+m),与字符集大小无关。

    Shift-And算法

    Shift-And算法和Shift-Or算法是维护一个字符串的集合,集合中的每个字符串既是模式串p的前缀,同时也是已读入文本的后缀。每读入一个新的文本字符,该算法即用位并行的方法更新该集合。该集合用一个位掩码D=dmdm-1..d1来表示。

    D的第j位被置为1时,称第j位为活动的,当且仅当p1…pj 是 t1…ti 的某个后缀,当D[m]=1时,就认为P已经于text有一个成功匹配。

    当读入下一个字符 ti+1,需要计算新的集合(位掩码) D'。当且仅当D[j]=1并且ti+1等于 pj+1时D'[j+1]=1。这是因为D[j]=1时有 p1…pj 是 t1…ti 的一个后缀,而当ti+1 等于 pj+1可推出p1…pj +1是 t1…ti+1 的一个后缀.这个集合可通过位运算在常数时间内更新。

    算法首先建立一个数组B,记录字母表中每个字符的位掩码bm..b1。数组长度为text串所属字符集长度(例如A-Z的话数组B的长度为26),如果P的第j位等于c则将B[c]中第j位置位1。

    首先置D=0m,对于每个新读入的文本字符ti+1,可以用公式

    进行更新。

    因为要预处理计算B,如果字符集很大的话,并不划算。如果m很长的话(大于机器字长),也很不方便。所以这种算法更适用于字符集较小,模式串小于机器字长的情况。当模式串的长度不超过几个机器字长时,公式的操作都能在常数时间内完成,这时Shift-And算法的时间复杂度为O(n)。

    Shift-Or算法是Shift-And的一种技巧性的实现。通过对位取反去掉公式中的掩码0m-11,加速对D'的计算。该算法使用反码来表示B中的位掩码和位向量D,因为左移操作会自动在D'右端引入0,于是空字符串就被纳入D'的计算中。Shift-Or算法D'对应的更新公式为:

    BM算法: BM算法是snort的采用从右向左比较的方法,同时应用到了两种启发式规则,即坏字符规则和好后缀规则,来决定向右跳跃的距离。

    坏字符规则(Bad Character):

    在BM算法从右向左扫描的过程中,若发现某个字符x不匹配,则按如下两种情况讨论:

    如果字符x在模式P中没有出现,那么从字符x开始的m个文本显然不可能与P匹配成功,直接全部跳过该区域即可。

    如果x在模式P中出现,则以该字符进行对齐。

    用数学公式表示,设Skip(x)为P右移的距离,m为模式串P的长度,max(x)为字符x在P中最右位置。

    好后缀规则(Good Suffix):

    若发现某个字符不匹配的同时,已有部分字符匹配成功,则按如下两种情况讨论:

     如果在P中位置t处已匹配部分P'在P中的某位置t'也出现,且位置t'的前一个字符与位置t的前一个字符不相同,则将P右移使t'对应t方才的所在的位置。

     ii. 如果在P中任何位置已匹配部分P'都没有再出现,则找到与P'的后缀P''相同的P的最长前缀x,向右移动P,使x对应方才P''后缀所在的位置。

    用数学公式表示,设Shift(j)为P右移的距离,m为模式串P的长度,j 为当前所匹配的字符位置,s为t'与t的距离(以上情况i)或者x与P''的距离(以上情况ii)。

     

     

    在BM算法匹配的过程中,取Skip(x)与Shift(j)中的较大者作为跳跃的距离。

    BM算法预处理时间复杂度为O(m+s),空间复杂度为O(s),s是与P, T相关的有限字符集长度,搜索阶段最好情况下的时间复杂度为O(n/m),最坏情况下时间复杂度为O(m*n)。

     

     

     

    BDM算法与BNDM算法

    BDM(Backward Dawg Matching)算法使用后缀自动机搜索子串。后缀自动机决定字符串U是否为模式串P的子串,有很多索引结构都可以在O(U)的时间内确定U是否为P的子串。

    总体来讲后缀自动机可以在O(|U|)时间内确定个字符串U是否为字符P的子串,可以识别模式串的所有后缀。从初始状态到某个终止状态的路径上字符组成的字符串是模式串P的一个后缀,模式串P的对应的后缀自动机可以O(m)内构建完成。

    算法过程:1. 首先构建模式串P的反转Prv=PmPm-1....P1对应的后缀自动机,反转的目的是为了得出P的前缀自动机。

    2. 算法使用后缀自动机在搜索窗口中从后向前搜索模式串的子串。搜索过程如果到达了终止状态,并且对应的不是整个模式中,则它在窗口中的位置存在变量last中,此时,可以说找到了当前识别的P的最长前缀,因为是P的反转的最长后缀。该前缀从last开始到窗口尾处结束。

    3. 搜索过程以下2种可能的方式结束:

    1. 在识别一个子串时失败了,即读入一个字符σ,在P的反转后缀自动机的当前状态没有σ转移,这时将窗口向右移动,使得它的起始位置与last对齐

    2. 抵达了窗口的起始位置,意味着整个模式串P被成功匹配,报告一个成功匹配,并像1一样移动窗口。

    BDM算法的最坏时间复杂度是O(mn)。但如果在文本中各个字符相互独立并且等概率出现的情况下,该算法能够达到最优的平均时间复杂度O()。

    BNDM(Backward Nondeterministic Dawg Matching)算法的搜索方法与BDM算法相同,但它使用了位并行来识别子串。与原始的BDM相比,BNDM更简单,内存用量更少,具有更好的引用局部性,并且易于扩展到更复杂的模式串的情形。

    在当前搜索窗口内,设已读入的字符串为u,BNDM算法维护一个集合,记录u在prv中的所有出现位置。同Shift-And算法一样,该集合可以用一个位向量D来表示。如果子串pj…pj+|u|-1等于u,那么D的第m-j+1位为1,表示p的位置j是一个活动状态。如果模式串的长度不超过机器字长w,那么该集合可以用一个机器字D=dm…d1表示。

    当读入一个新的文本字符σ时,要从D更行到D'。D'的一个活动状态j对应于在模式串中的一个起始位置,即u出现在模式串的j+1位,在模式串的j位出现。

    同Shift-And算法一样,BNDM算法预先计算一张表B,用位掩码记录每一个字符在p中出现的位置。D的更新公式:。

    BNDM与BDM使用的搜索方法是一样的,区别在于前者用位并行技术进行子串搜索,后者使用后缀自动机。每当比特位dm是活动的时候,窗口中的当前位置就用变量last保存。主要代码如下:

    int BNDMMatch(byte* pSrc, int nSrcSize, byte* pSubSrc, int nSubSrcSize){

    int B[256] = {0};

    for(int i = 0; i < nSubSrcSize; i++){

    skip[pSubSrc[i]] |= 1 << (nSubSrcSize - 1 - i);

    }

    int nPos = 0;

    while(nPos <= nSrcSize - nSubSrcSize) {

    int j = nSubSrcSize -1;

    int last = nSubSrcSize;

    unsigned int D = -1;

    while(D) {

    D &= skip[pSrc[nPos + j]];

    if (D & (1<<(nSubSrcSize-1))) {

    if (j > 0) {

    last = j;

    }

    else {

    return nPos;

    }

    }

    j--;

    D <<= 1;

    }

    nPos += last;

    }

    return -1;

    }

    BNDM和BDM算法的时间复杂度相同,最坏时间复杂度O(mn),也都具有最优的平均时间复杂度O()。

    BOM算法

    当字符串长度大于机器字长w时,虽然BDM算法可以实现,但是后缀自动机复杂的构造过程使得BDM算法并不那么实用。

    单模式字符串匹配算法:

    搜索方向

    特点

    代表算法

    方法

    搜索方式

    备注

    前缀搜索

    正向搜索,搜索窗口中文本和模式串的最长公共前缀

    KMP算法

    确定性自动机

    逐个读入字符

     

    Shift-And

    位并行,非确定行自动机

    后缀搜索

    后向搜索,搜索窗口中文本和模式串的最长公共后缀,可以跳过一些文本字符,难点在于如何安全地移动窗口

    BM

    预先计算三个函数用于确定安全跳跃距离

    滑动窗口

     

    Horspool

    改进了BM中的函数,能产生更大的跳跃距离,特别适合较大的字母表

    子串搜索

    后向搜索,判断窗口中文本的后最是否是模式串的子串

    BDM

    后缀自动机

    搜索窗口中文本的最长后缀

     

    BNDM

    位并行

    BOM

    Factor Oracle自动机,适用于较长的模式

  • 相关阅读:
    机器学习面试
    网易有道2017内推编程题2道
    老曹眼中的网络编程基础
    MySQL索引背后的数据结构及算法原理
    [oracle] oracle-ibatis-整理
    [oracle] oracle-myibatis-整理
    [mysql] mysql-myibatis-整理
    [JS] selector 背景选择器
    [android] AndroidManifest.xml 详解
    [Eclipse] 项目编码
  • 原文地址:https://www.cnblogs.com/zijin/p/3356717.html
Copyright © 2011-2022 走看看