串的模式匹配

朴素模式匹配

朴素模式匹配的时间复杂度为O(m*n),其中m为主串的长度、n为模式串的长度。
原因:
例如主串为 s = “aabaabaaf” m = 9
模式串为 t = “aabaaf” n = 6
双指针i,j: i遍历主串,j遍历模式串
若i和j对应位置的字符相等,i++、j++;若不相等 i = i - j +1、j = 0;
例如:i = 0 、j = 0时,直到s和t都遍历到索引位置5处,‘b’ != ‘f’,j回溯到0,i = i - j + 1,重新继续遍历直至j>=t.size()(未找到匹配串)或找到匹配串。
时间复杂度:上例需要匹配 4 * 6 = 24次;((m - n) + 1) * n;
对于其他字符串,最好情况下,第一次遍历就找到匹配串O(1);最坏情况下,每次遍历到最后一位才发现不同,直到最后一次遍历才完全匹配,时间复杂度为O(m * n);

KMP模式匹配算法

由朴素模式匹配引出KMP模式匹配算法:继续使用主串 s = “aabaabaaf”,模式串 t = “aabaaf”
发现遍历到索引位置5时(‘b’ != ‘f’),j不必回溯到索引0,因为"aabaabaaf"中索引5之前的字符串"aabaa"与模式串对应位置"aabaa"完全相同,又发现模式串中存在前缀和后缀完全相同的部分"aa" = “aa”,所以j到’b’位置(索引为2)的位置继续开始遍历,i同样也不要回溯,正好匹配成功。

:前缀指字符串中包含首字符的所有子串(不考虑尾字符);后缀指子符串中包含尾字符的所有子串(不考虑首字符)。
例如:"aabaaf"的前缀有:a、aa、aab、aabaa、aabaa;后缀有f、af、aaf、baaf、abaaf、abaaf;

前缀表

前缀表在KMP算法中就是作为next[],用来判断当前不匹配时,模式串的j指针回溯到何位置。

如何计算模式串的前缀表:最长长度相等前后缀
例如: t = “aabaaf”
‘a’, next[0] = 0;
“aa” , next[1] = 1;
“aab”, next[2] = 0;
“aaba”, next[3] = 1;
“aabaa”, next[4] = 2;
“aabaaf”, next[5] = 0;
结论
t = “aabaaf”
next 010120
注:j回溯到next[j-1]的位置

代码求解next[]数组:

void getNext (int next[], string &t) {
    //四步
    //1、初始化
    //i表示后缀末尾 
    //j表示前缀末尾、同时表示最长相等前后缀的长度
    int j = 0;
    next[0] = 0;
    for (int i = 1; i < t.size(); ++i) {
        //2、前后缀不相同
        while(j > 0 && t[i] != t[j]) {
            j = next[j - 1];
        }
        //3、前后缀相同
        if(t[i] == t[j]){
            ++j;
        }
        //4、更新next[]  
        next[i] = j;
    }
}

完整kmp算法:

#include <iostream>
#include <string>
using namespace std;

void getNext (int next[], string &t) {
    //四步
    //1、初始化
    //i表示后缀末尾 
    //j表示前缀末尾、同时表示最长相等前后缀的长度
    int j = 0;
    next[0] = 0;
    for (int i = 1; i < t.size(); ++i) {
        //2、前后缀不相同
        while(j > 0 && t[i] != t[j]) {
            j = next[j - 1];
        }
        //3、前后缀相同
        if(t[i] == t[j]){
            ++j;
        }
        //4、更新next[]  
        next[i] = j;
    }
}

bool kmp(string &s, string &t) {
    int i = 0, j = 0;
    int next[t.size()];
    getNext(next, t);
    while (i < s.size() && j < t.size()) {
        // j回溯到0时 i+1;
        if (j == 0 || s[i] == t[j]) {
            ++i;
            ++j;
        }else{
            j = next[j - 1];
        }
    }
    if ( i > s.size()) {
        return false;
    }
    return true;
}

int main()
{
    string s = "aabaabaaf";//主串
    string t = "aabaaf"; //模式串
    cout << kmp(s, t) << endl;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值