字符串的匹配算法

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

朴素的模式匹配算法

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

让我们来分析一下,最好的情况是什么?那就是第一开始就成功,时间时间复杂度为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

 

posted @ 2020-02-28 22:53  精神小伙儿  阅读(241)  评论(0编辑  收藏  举报