• 字符串匹配之KMP算法


    一.简单的字符串匹配

    我们经常需要在某一串字符中查找是否存在给定的子串,我们将给定的子串叫做模式,查找子串的问题叫做模式匹配。

    进行字符串的模式匹配,最简单的方法就是依次将子串与给定的字符串依次进行比较。算法如下:

    int patFind(const char* str, const char* pat){
        int pos1,pos2;
        bool isfind;
        int i=0;
        pos1=i;
        pos2=0;
        while(str[pos1]!=''&&pat[pos2]!=''){
            if(str[pos1++]!=pat[pos2++]){
                pos1=++i;
                pos2=0;
            }
        }
        if(pat[pos2]==''){
            return i;
        }
        return -1;
    }

     简单的匹配算法存在回溯现象,复杂度为O(m*n)

    二.KMP算法的思想

    在匹配的时候经常会碰到回溯,而根据对Pattern的先验计算,能避免回溯,提高匹配效率,KMP算法就是通过对Pattern进行先验计算来避免回溯的算法。

    如p0...pi与ts...ts+i匹配,pi+1与ts+i+1不等,这时按照简单的匹配算法,将继续第s+1趟比较比较p0...pi-1...pm与ts+1...ts+i...ts+1+m,如果这两者匹配,那么有

    p0...pi-1=ts+1...ts+i=p1...pi

    这时,如果已知两者不等,那么就不用做第s+1趟比较了。同理,如果已知p0...pi-2不等于p2...pi那么就不用做第s+2趟比较。

    依此类推,直到找到最大的k,使得p0...pk=pi-k...pi,这时可以直接做第s+i-k趟比较,并因为已知p0...pk=pi-k...pi=ts+i-k...ts+i了,直接比较pk+1和ts+i+1即可。

    这时我们可以定义一个失效函数f(i),比较至pi,使得pi匹配,pi+1不匹配时,其值等于使得p0...pk=pi-k...pi且小于i的最大k值,如果找不到则值为-1.

    对于模式pat=abaabcaba,

    f(0)为-1

    因为p0!=p1, f(1)为-1

    因为p0=p2,f(2)为0

    因为p0=p3,f(3)为0

    因为p0p1=p3p4,f(4)为1

    f(5)为-1

    f(6)为0

    f(7)为1

    f(8)为2

    如果第s趟比较在第i位失配,则下一趟直接比较pf(i-1)+1和ts+i

    三.失效函数的计算

     已知f(j-1)=k,则有p0...pk=pj-1-k...pj-1,这时如果pk+1=pj,则f(j)=k+1,如果不等,则继续找f(k),因为p0...pf(k)=pj-1-f(k)...pj-1,这时如果pf(k)+1=pj,则f(j)=f(k)+1,依此类推。我们可以得到下面的方法来计算失效函数。

    void failFunc(const char* pat, int* fail){
        fail[0]=-1;
        int len=strlen(pat);
        int k;
        for(int j=1;j<len;j++){
            k=fail[j-1];
            while(k>=0&&pat[k+1]!=pat[j]){
                k=fail[k];
            }
            if(pat[k+1]==pat[j]){
                fail[j]=k+1;
            }
            else{
                fail[j]=-1;
            }
            cout<<fail[j]<<endl;
        }
    }

    四.KMP模式匹配算法的实现

    int kmpFind(const char* str, const char* pat){
        int pos1=0,pos2=0;
        int patlen=strlen(pat);
        int* fail=new int[patlen];
        failFunc(pat,fail);
        while(str[pos1]!=''&&pat[pos2]!=''){
            if(str[pos1]==pat[pos2]){
                pos1++;
                pos2++;
            }
            else if(pos2==0){
                pos1++;
            }
            else{
                pos2=fail[pos2-1]+1;
            }
        }
        if(pat[pos2]==''){
            return pos1-patlen;
        }
        return -1;
    }

    虽然计算失效函数需要浪费一定的时间和空间,可是从代码我们可以看到KMP没有任何的回溯,时间复杂度仅为(m+n),当模式比较长时KMP算法比起简单的模式匹配有显著的优势。

    运行结果:

    int _tmain(int argc, _TCHAR* argv[])
    {
        char str[] = "ababaabaabaabcabaaaabbcccc";
        char pat[] = "abaabcaba";
        
        cout<<str<<endl;
        cout<<pat<<" can be found at position:"<<kmpFind(str,pat)<<endl;
        
        return 0;
    }
  • 相关阅读:
    vue学习记录(四)---router的运用
    node.js 框架express关于报错页面的配置
    node.js 的热更新
    yii2 Menu组件的使用
    node.js 框架express有关于router的运用
    node.js 的页面渲染方法ejs
    什么是mybatis
    如何清除maven中下载失败的包
    hibernate 执行步骤
    什么是ORM
  • 原文地址:https://www.cnblogs.com/studynote/p/3383004.html
Copyright © 2020-2023  润新知