KMP算法

KMP 算法#

KMP算法是一种用于快速找出匹配的子字符串位置的算法。通过记录要匹配的字符串各位置前面子字符串最长相同前后缀的信息,达到加速匹配过程的效果。
前缀字符串是指从前向当前字符串位置数的字符串;后缀字符串是指从后向当前字符串数的字符串。这里是不包含自身位置的。例如字符串"aaabaa",其相同前缀缀长度是

a a a b a a b
-1 0 1 2 0 1 2

这里我们规定最前的字符对应的是-1,第二个字符因为前面字符串只有一个字符,所有记作0。
第三个字符是a,它前面的字符串是"aa",将这个字符串从前数和从后数,得到的最长字符串长度是1(因为不包含自身),所以记作1.
第四个字符是b,它前面的字符串是"aaa",那么从前数最长的前缀字符串是"aa",从后数,最长的后缀字符串是"aa",且两者匹配,那就记作2。
同理,第5个字符a,它前面的字符串是"aaab",因为从前数第一个字符是"a",从后数第一个"字符是"b",直接不匹配,那它就是0。

/**
 *@brief 计算子串每个位置的最长匹配前后缀的长度
 * 
 * @param m     要计算的子串
 * @param next_arr 返回的长度数组,实际上也是记录了匹配到的最长前缀的后面一个元素下标
 * @param index     当前要计算的下标位置
 * @param cn        index的前一个字符最长匹配前后缀的长度,也就是index - 1位置对应的最长匹配前后缀的前缀的后面一个元素的下标
 * @return vector<int>
 */
vector<int> getNextArray(string m) {
    if(m.size() == 1) {
        return vector<int> {-1};
    }
    else if(m.size() == 2) {
        return vector<int> {-1, 0};
    }
    
    vector<int> next_arr{-1, 0};    //前两个位置规定为-1和0

    //next_arr数组可以用动态数组的方法
    //i-1位置记录的地址上的字符如果和i位置上的字符相同,第i位置记录的数值就应该是第i-1位置的数值加一;
    //如果不相同就继续往前跳,直到找到一个位置上的字符和第i-1位置上的相同,第i位置上的数值就等于该位置上的数值加一,如果到头了也不等,就等于零
    int index = 2;  //第i位置下标
    int cn = 0;     //哪个位置和i-1处的字符比较
    while (index < m.size()) {
        if (m[index - 1] == m[cn]) {    //如果相等
            next_arr.push_back(cn + 1);
            cn = next_arr.back();       //下个比较的位置
            ++index;    //开始下个位置
        }
        else if (cn > 0) {  //如果不相等,且没到头,cn向前跳
            cn = next_arr[cn];
        }
        else {  //到头了
            next_arr.push_back(0);
            ++index;
        }
    }
    return next_arr;
}

int KMP (string s, string m) {
    //s是较长字符串,m是要匹配的字符串
    if(s.size() == 0 || m.size() == 0 || s.size() < m.size()){
        return -1;
    }
    vector<int> arr_s;
    vector<int> arr_m;
    vector<int> nextArr = getNextArray(m);    //记录匹配字符串对应的数组
    int index_s = 0;
    int index_m = 0;
    //O(N)
    while (index_s < s.size() && index_m < s.size()) {
        //对应位置匹配就同时++
        if(s[index_s] == m[index_m]) {
            index_s ++;
            index_m ++;
        }
        //m到头了也没匹配上,那么就只能s向后移动了
        else if (index_m == 0) {
            index_s ++;
        }
        //如果没匹配上,根据next_arr记录的位置向前跳
        else {
            index_m = nextArr[index_m];
        }
    }
    //最终是哪个下标先越界,index_m越界说明找到了子字符串,index_s越界了说明没找到
    return  index_m == m.size()?  index_s - index_m: -1;
}

作者:cwtxx

出处:https://www.cnblogs.com/cwtxx/p/18718171

版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。

posted @   cwtxx  阅读(4)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
more_horiz
keyboard_arrow_up dark_mode palette
选择主题
menu
点击右上角即可分享
微信分享提示