改进的KMP

KMP改进主要是对next数组进行改进
这里写图片描述
这里写图片描述
如图当s[3]与p[3]不匹配时,根据next[3]=1,所以继续把s[3]与p[1]进行比较,即把模式串右移j-k=3-1=2位,此时s[3]与p[1]还是不等。其实仔细想一想,这是必然的,为什么呢?
因为当j=3时,p[j]=p[next[j]]即p[3]=p[1],问题就出在这里,当s[i]!=p[j],又因为p[j]=p[next[j]],所以p[next[j]]!=s[i]。所以可以对程序进行优化。如果出现了p[j] = p[ next[j] ],则需要再次递归,即令next[j] = next[ next[j] ]。
优化之前

public static int [] getNext(String p){
        int plen=p.length();
        char[] parray = p.toCharArray();
        int [] next=new int [plen];
        next[0]=-1;
        int k=-1,j=0;
        /**
        首先要明白next[j]=k是什么意思,简单概述就是:当主串s和模式串p比较时,当s[i]!=p[j]时,
        (我们不采用蛮力法时)我们的主串s的s[i]字符接下来该和模式串的p[k]字符进行比较,即我们
        不再是像蛮力法那样,每次把模式串右移一个字符,再回溯到主串的下一个字符进行比较。采用KMP
        算法,我们不需要回溯主串,而是直接把模式串右移j-k(j-next[j])个位置,进行下次比较。
        也就是说当s[i]!=p[j]的时候,接下来我们让s[i]和p[k](p[next[j]])进行比较。
        那么我们我们应该怎么求解next数组呢?已知next[j]=k,我们可以采用递归的方式求next[j+1]的值
        注意next[j]=k意味着p[0]p[1]p[2]....p[k-1]=p[j-k]....p[j-1],=左右各有k个值
        所以当求next[j+1]的时候,就需要比较p[0]p[1]p[2]....p[k-1]p[k]=p[j-k]....p[j-1]p[j]
        又因为p[0]p[1]p[2]....p[k-1]=p[j-k]....p[j-1]相等,所以只需要比较p[k]和p[j]
        假如p[k]=p[j],则next[j+1]=k+1,
        假如p[k]!=p[j],令k=next[k],此时再比较p[j]与p[k],假如如果不相等,则继续递归前缀索引,
        令 k=next[k],继续判断,直至k=-1(即k=next[0])或者p[j]=p[k]为止。
         */

        while(j<plen-1){//对于数组next长度为plen所以当一个循环不满足条件时,j=plen-1,刚好是next数组的最后一个。
            if(k==-1||parray[j]==parray[k]){//因为不可能p[0]和p[-1]比较,所以用k==-1
                k++;
                j++;
                next[j]=k;//next[j+1]=k+1
            }else{
                k=next[k];
            }
        }
        return next;
    }

优化之后的

public static int[] getNext1(String p){
        int plen=p.length();
        char[] parray = p.toCharArray();
        int [] next=new int[plen];
        next[0]=-1;
        int k=-1,j=0;
        while(j<plen-1){
            if(k==-1||parray[j]==parray[k]){
                k++;
                j++;
                if(parray[j]!=parray[k]){//相当于判断p[j]!=p[next[j]]
                    next[j]=k;
                }else{
                    next[j]=next[k];
                }   
            }else{
                k=next[k];
            }
        }
        return next;
    }
### 改进和优化KMP算法的C++实现 #### 1. 预处理模式串以减少冗余计算 为了提高效率,在构建部分匹配表(也称为前缀函数)时可以采用更高效的方法来避免不必要的重复运算。通过一次遍历模式字符串并记录最长相等前后缀长度,可以在O(m)时间内完成预处理工作[^1]。 ```cpp void computeLPSArray(const string& pattern, int* lps) { int length = 0; // 当前已知的最大相同前后缀长度 lps[0] = 0; int i = 1; while (i < pattern.size()) { if (pattern[i] == pattern[length]) { ++length; lps[i] = length; ++i; } else { if (length != 0) { length = lps[length - 1]; } else { lps[i] = 0; ++i; } } } } ``` #### 2. 使用位操作加速字符比较过程 当目标文本非常大而模式较短的情况下,可以通过预先加载多个字节到寄存器中利用SIMD指令集来进行批量对比测试从而加快速度。不过这种方法对于大多数应用场景来说可能并不实用,因为其带来的性能增益往往被额外增加复杂度所抵消。 #### 3. 减少内存分配次数 如果频繁创建临时对象或数组,则会带来显著开销。因此应该尽可能重用已经存在的数据结构而不是每次都重新初始化它们;另外还可以考虑使用静态变量代替动态申请空间的方式降低堆栈切换成本。 ```cpp class KMPSearcher { private: static constexpr size_t MAX_PATTERN_LENGTH = 1000; public: void setPattern(const std::string &pat); bool searchInText(const char *text); private: int lps[MAX_PATTERN_LENGTH]; // 前缀函数存储位置 }; ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值