• 字符串的匹配算法


    问题:在主串中寻找字串的位置

    朴素的模式匹配算法

    简单的说,就是对主串的每一个字符作为字串的开头,与要匹配的字符串进行匹配。对主串做大循环,每个字符开头做字串的长度的小循环,直到匹配成功或者全部遍历为止。

    让我们来分析一下,最好的情况是什么?那就是第一开始就成功,时间时间复杂度为O(1) ;最坏呢,就是每次循环到子串最后一位才发现不对,时间复杂度为O((n-m+1)*m)

    效率确实太低

    于是就有了下面的算法

    KMP模式匹配算法

    避免不必要的回溯

    比如 a = "abcabcabc"   t = "abcabx"

    循环到 j = 6 时发现错误,这时t中,有两个ab,如果按朴素模式的化此时 j = 2;但是,刚才我们已经遍历过 a[2] = t[2] = b 不可能再等于 t[2] ,要避免这多余的回溯,如果 j 应该等于3

    应该t中 t[1-2] = t[4-5] 而第 6为失败,说明检测第6位时应该跳过t[1-2] 使得 j = 3;

    abcabcabc

    abcabx

          abcabx  从c开始匹配

     

    这时就需要一个next[] 数组来记录 j 该是什么

    next[j] = 1 到 j-1 中 前缀 和 后缀 相同字母的个数+1

    下面是 构造next数组的代码: ///尽力了。。

    void GetNext(string s,int next[])
    {
        next[0] = -1;
        int len  = s.length();
        int i = 0;//s 的下标
        int j = -1;
        while(i<len)
        {
            if(s[i]==s[j]||j==-1)//最大前缀后缀公共字串长度为j,
                                //那么比较此为和第j+1位字符是否相同,j+1位 下标为j
            {                    // 相同则最大公共长度+1
                i++;
                j++;
                next[i] = j;
            }
            else//  不同 则 找 和前j项 能组成的最大公共长度,
                //而前j项最大字串为next[j] 则判断第next[j]+1项和i是否相同,
                //next[j]+1项的下标为next[j]
            {
                j = next[j];
            }
        }
    
    }

    KMP函数就比较简单 和朴素算法相同 只是 小循环 回溯时发生了变化 

    int KMP(string s,string p,int next[])
    {
        GetNext(p,next);
    
    
        int i = 0;
        int j = 0;
        int slen = s.length();
        int plen = p.length();
        while(i<slen&&j<plen)
        {
            if(j==-1||s[i]==p[j]) // j = -1 表示第一个就不匹配,则比较下一个
                                  // ||当字符相同时比较下一个
            {
                i++;
                j++;
            }
            else
            {
                j = next[j];// 注意next[0] = -1 需要特判
            }
        }
        if(j==plen)
        {
            return i-j;
        }
        else
        {
            return -1;
        }
    }

    完整代码

    #include<iostream>
    #include<string>
    #include<cstring>
    using namespace std;
    
    void GetNext(string s,int next[])
    {
        next[0] = -1;
        int len  = s.length();
        int i = 0;//s 的下标
        int j = -1;
        while(i<len)
        {
            if(s[i]==s[j]||j==-1)//最大前缀后缀公共字串长度为j,
                                //那么比较此为和第j+1位字符是否相同,j+1位 下标为j
            {                    // 相同则最大公共长度+1
                i++;
                j++;
                next[i] = j;
            }
            else//  不同 则 找 和前j项 能组成的最大公共长度,
                //而前j项最大字串为next[j] 则判断第next[j]+1项和i是否相同,
                //next[j]+1项的下标为next[j]
            {
                j = next[j];
            }
        }
    
    }
    
    int KMP(string s,string p,int next[])
    {
        GetNext(p,next);
    
    
        int i = 0;
        int j = 0;
        int slen = s.length();
        int plen = p.length();
        while(i<slen&&j<plen)
        {
            if(j==-1||s[i]==p[j]) // j = -1 表示第一个就不匹配,则比较下一个
                                  //||当字符相同时比较下一个
            {
                i++;
                j++;
            }
            else
            {
                j = next[j];
            }
        }
        if(j==plen)
        {
            return i-j;
        }
        else
        {
            return -1;
        }
    }
    
    
    int main()
    {
        string s,p;
        cin>>s;
        cin>>p;
        int next[100]={0};
    
        int flag = KMP(s,p,next);
        cout<<flag<<endl;
        return 0;
    
    }
    View Code

     

  • 相关阅读:
    SIT/UAT测试
    Oracle密码过期设置和修改密码问题
    1、查询速度慢的原因很多,常见如下几种:
    dbs:apple-notes
    值不能为 null 或为空。参数名: linkText
    Visual Stadio 2015创建WebApplication应用和运行赏析
    HTTP 错误 500.19
    Introduction to ASP.NET Web Programming Using the Razor Syntax (C#)
    vs2015-Azure Mobile Service
    6.1.1 验证注解的使用
  • 原文地址:https://www.cnblogs.com/subject/p/12379876.html
Copyright © 2020-2023  润新知