我眼中的KMP

我眼中的KMP

1. KMP可以用来解决什么问题

KMP主要应用在字符串匹配上。KMP的主要思想是:当模式串与主串出现不匹配的字符时,可以知道一部分已经匹配的文本内容,可以利用这个避免重头开始匹配,大大的节省了时间。

2. KMP怎么用

KMP算法核心在于next数组的求解,但在求解前后缀、最长公共前后缀。下面分别对这三个概念加以阐述:

1. 前缀和后缀

假设你的字符串是 aabaaf

啥叫前缀呢?前缀就是带有第一个字符但不包括最后一个字符的所有顺序子串。那么前缀就是:aaaaabaabaaabaa

啥叫后缀呢?后缀就是带有最后一个字符但不包括第一个字符的所有顺序子串。那么后缀就是:

fafaafbaafabaaf

2. 最长公共前后缀

最长公共前后缀就是:从所有子串的首位开始,比较当前子串相同前后缀的最大长度。还是以上面字符串为例:aabaaf

子串最长公共前后缀
a0
aa1
aab0
aaba1
aabaa2
aabaaf0

事实上,最长公共前后缀就是前缀表、next数组等。

next数组的作用:回退!!!它记录了模式串和主串不匹配时,模式串应该从哪里重新开始匹配。

3. next数组

next数组就是前缀表,对应到子串中,可以表示如下:

aaaaabaabaaabaaaabaaf
010120

即next数组为:int* next = {0, 1, 0, 1, 2, 0};

4. 举例

假设主串为 aabaabaaf,模式串为 aabaaf,当匹配的时候,发现匹配到f的时候发现匹配不成功,然后就去找next数组,就要找当前字符的前一个next数组的数字,我们可见匹配不成功的是f,找f前一个next数组的数字下标,一查表,发现是2,所以就回退到第2个字符处重新匹配,可见,就从b重新开始匹配;从b开始匹配之后,发现就成功匹配了所有模式串。

3. 用C++编写代码

class KMP{
	public:
    	void GetNext(int* next, const string& s){
            int j = 0;
            next[0] = j;
            for(int i = 1; i < s.size(); ++i){
                while(j > 0 && s[i] != s[j]){
                    j = next[j - 1];
                }
                if(s[i] == s[j])
                    j++;
                next[i] = j;
            }
        }
    
    int strStr(string& mainStr, string& patternStr){
        if(patternStr.size() == 0)
            return -1;
        int next[pattern.size()];
        GetNext(next, patternStr);
        int j = 0;
        for(int i = 0; i < mainStr.size(); ++i){
            while(j > 0 && mainStr[i] patternStr[j])
                j = next[j - 1];
            if(mainStr[i] == patternStr[j])
                ++j;
            if(j == patternStr.size())
                return i - patternStr.size() + 1;  // 如果匹配成功,res返回的是模式串在主串中首次匹配的位置。
        }
        return -1;
    }
};

int main(){
    string mainStr = "aabaabaaf";
    string patternStr = "aabaabaaf";
    KMP s;
    int res;
    res = s.strStr(mainStr, patternStr);
    cout << res << endl;         
    return 0;
}

4. 时间复杂度

假设主串长度 m,模式串长度 n:

如果暴力匹配字符串的话,时间复杂度是 O(m * n);

如果使用KMP算法的话,时间复杂度是 O(m + n)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值