字符串匹配-KMP算法

KMP算法,字符串匹配算法,给定一个主串S,和一个字串T,返回字串T与之S匹配的数组下标。

在学KMP算法之前,对于两个字符串,主串S,和字串T,我们根据暴力匹配,定义两个指针,i指向主串S的起始,j指向字串T的起始,依次比较,如果主串i位置的值等于子串j位置的值,i++,j++。直到i位置的值和j位置的值不相同,i回溯到起始位置+1,同时字串T的起始位置后移到i所在位置。直到匹配成功,或者子串T后移长度+T本身长度>S主串的长度。这个暴力求解的复杂度,因为有i的回溯,需要2层循环i,j的移动,因此时间复杂度为T(n*m),n是S的长度,m是T的长度。

根据暴力匹配的思想,我们接下来分析一下KMP算法。同样两个字符串按位比较,而KMP算法的核心在于,当主串的i位置的值和子串的j位置的值不同时,主串S,i前面的字符串与字串T,j前面的字符串已经匹配相等,因为两者相等,所以只需要拿出子串T前面的字符串,根据T前面的字符串来计算一个nextj数组,将j回溯即可。问题便转换为求子串的nextj数组。那么nextj数组的求法为,i前面的字符串,分别取前缀和取后缀,如果前缀的长度=后缀的长度,则j的值=字符串缀长+1存入next数组。否则,j回溯给nextj。直到j=字串长度,则next数组计算完成。后续根据i不回溯,j从next数组里取值,便可将字串T和主串S进行匹配,直到字串T移出到主串S的长度,匹配成功返回i-j下标,匹配失败返回-1。因为KMP算法简化了问题的求解,将难点转换为求next数组,并且i不回溯,可以做到边移动边匹配。因此,时间复杂度为T(n+m)

下面给两个实例:

1:给定2个字符串,主串S,子串T,如果T匹配S,则返回在S当中的下标。

java 复制代码
  public static void main(String[] args) {

        String S  = "abababcabcabc";
        String T  = "bcabc";


        int pos = KMP(S,T);

        System.out.println(pos);


    }

    private static int KMP(String s, String t) {
        int i = 0;//i指向S
        int j = 0;//j指向T

        while (i<s.length()&&j<t.length()){
            if(j==-1||s.charAt(i)==t.charAt(j)){//为什么j==-1,i和j也需要后移,当j==-1,说明字串和主串的起始点在0,
                i++;//i后移
                j++;//j后移
            }else{
                j = getNext(t)[j]; //j根据t求一个next数组,next数组的作用就是j根据内部的值回溯。
            }
        }

        if(j == t.length()){  //j已经等于t的长度了,说明匹配结束了。
            return i-t.length();  //字串起始点就是i-j或者i-t.length()
        }else{
            return -1;//匹配失败了。
        }

    }

    private static int[] getNext(String t) {
        int i = 0;//next数组下标,初始值0
        int j = -1;//j指向字符串t,初始值-1
        int [] next = new int[t.length()];//构造next数组,长度为t的长度。
        next[0] = -1;//next数组从1开始存值,即0号位置存默认值-1;
        while (i<t.length()-1){
            if(j==-1||t.charAt(i)==t.charAt(j)){//j==-1表示从头开始遍历t,或者t的前缀==t的后缀,都要将j+1存入next数组
               i++;
               j++;
               next[i] =  j;  //如果后缀==前缀,将j+1,即j++的值存入next数组。
            }else {
               j = next[j]; //如果后缀!=前缀,j回溯到next[j]位置
            }
        }
        return next;
    }

输出结果:

5

完全正确。

2:给定2个字符串,主串S,子串T,返回子串T在主串S中出现的次数。

求次数的问题,将原生KMP算法,变化为,当j==t.length()的时候,匹配有效,计数器count++,j回溯到next数组的j-1位置上,再对其+1。+1的目的是next数组值=子串前缀/后缀的长度+1

java 复制代码
    public static void main(String[] args) {

        String S  = "abababcabcabc";
        String T  = "bcabc";


        int count= KMP(S,T);

        System.out.println(count);


    }

    private static int KMP(String s, String t) {
        int i = 0;//i指向S
        int j = 0;//j指向T
        int count = 0;//计数器
        int[] next = getNext(t);

        while (i<s.length()&&j<t.length()){
            if(j==-1||s.charAt(i)==t.charAt(j)){//为什么j==-1,i和j也需要后移,当j==-1,说明字串和主串的起始点在0,
                i++;//i后移
                j++;//j后移
            }else{
                j = next[j]; //j根据t求一个next数组,next数组的作用就是j根据内部的值回溯。
            }
            if(j==t.length()){//匹配到了
                count++;//计数器++
                j = next[j-1]+1;//j回溯到next数组的[j-1]=前缀长度,而j的值=对其+1
            }
        }
        return count;
    }

    private static int[] getNext(String t) {
        int i = 0;//next数组下标,初始值0
        int j = -1;//j指向字符串t,初始值-1
        int [] next = new int[t.length()];//构造next数组,长度为t的长度。
        next[0] = -1;//next数组从1开始存值,即0号位置存默认值-1;
        while (i<t.length()-1){
            if(j==-1||t.charAt(i)==t.charAt(j)){//j==-1表示从头开始遍历t,或者t的前缀==t的后缀,都要将j+1存入next数组
                i++;
                j++;
                next[i] =  j;  //如果后缀==前缀,将j+1,即j++的值存入next数组。
            }else {
                j = next[j]; //如果后缀!=前缀,j回溯到next[j]位置
            }
        }
        return next;
    }

输出结果

2

求出现次数区别在于这里的不同:

相关推荐
GreenTea3 小时前
vLLM 与 SGLang KV Cache 底层实现机制深度调研报告
前端·后端·算法
Bode_20023 小时前
多资源规划(含生产调度、库存管理及产能规划)的创新优化算法
算法·调度
土司大王4 小时前
LeetCode hot100——35.搜索插入位置:Java 二分模板、左闭右开区间与插入点分析
java·算法·leetcode
微三云生态系统架构师-彭丹4 小时前
远方好物S2B2C系统架构:一级分销与保证金托管的合规技术实现
人工智能·算法
土司大王4 小时前
LeetCode hot100——34.在排序数组中查找元素的第一个和最后一个位置:Java 二分模板、边界分析
java·算法·leetcode
罗西的思考6 小时前
机器人模型(WM / WAM / VLA)综合分析与对比:从「看」到「想」再到「做」
人工智能·算法·机器学习
小哈里7 小时前
【知识】从学科到实践:自然・工程・社科・人文|算法・研发・产品・品牌设计
程序人生·算法·产品·设计·工程
小π军7 小时前
最大重叠区间数量
数据结构·算法
镜像视界(浙江)科技有限公司8 小时前
《视频孪生之上:二维展示终结,三维空间计算重构城市逻辑》——跨摄像连续表达 × 三角测量厘米级定位 × 动态轨迹建模,构建新一代城市空间
大数据·人工智能·算法·矩阵·音视频·空间计算
a187927218318 小时前
【算法】双指针与滑动窗口(二):滑动窗口——吃进、判定、吐出
算法·leetcode·双指针·滑动窗口·原理·模板·算法讲解