KMP算法学习

本文详细介绍了如何在Java中实现KMP算法,重点讲解了next数组的构建及其在避免冗余比较中的作用。通过实例和代码演示,帮助读者理解KMP算法的工作原理和核心思想。
摘要由CSDN通过智能技术生成

由于编程语言不同以及对于next数组的定义不同,会导致学习kmp算法的过程中有很多疑惑,这篇文将讲述java语言实现kmp算法。

学习KMP算法首先要了解的两个内容,一是kmp算法本身,二是next数组。

kmp算法

KMP(Knuth-Morris-Pratt)算法是一种用于在文本中查找模式的高效字符串匹配算法。该算法的关键思想是通过预处理模式字符串,构建一个部分匹配表,然后利用这个表在匹配过程中避免不必要的比较,从而提高匹配的效率。

学习kmp算法前,可以先去了解普通的模式匹配法,这样更可以体会到kmp算法的强大。

第一步:

在主串中匹配子串,当匹配到第五个字符时,会发现A与C并不匹配。在传统的算法中,下一步会将子串的A与主串的B进行匹配,这样显然很繁琐,那么kmp算法是怎么样的呢?那么看下图。

kmp算法会直接将箭头所指向的两个值进行匹配,跳过了前面的步骤。那么为什么可以这样。

这时就需要先引入一个叫作next数组的东西,先不管它怎么来的,后面会讲。

子串下面的数字就是next数组,当A与C不匹配时,上一个匹配字符也就是B,所对应的数字是2,那么就移动子串将子串中索引为2的字符(A)与主串中索引为4的字符(A)进行匹配。

那么接下来讲解next数组。next数组代表的是前缀与后缀的最长匹配数

以ABABC为例,当只有一个A时,没有字符与它匹配,所以是0,接下来是AB,A与B并不相同,所以B下也是0,ABA时,A与A相同所以是1,ABAB时AB与AB相同所以是2,当ABABC时,前缀与后缀没有相同的串时,值为0。这就是next数组的求法。

那么算法应该怎么写呢

看这个情况,当B与C不匹配时该怎么办,看上一个匹配的字符A的next值为3,当前的前缀长度为3,那么看前缀的第三个字符,值为1,代表这个前缀只有一个匹配字符长度为1,说明第一个字符与第三个字符是相等的,而第三个字符又于第七个字符相等,因为前缀与后缀相等,所以可以直接开始匹配第二个字符B与第八个字符B。

同样的道理,再回到主串与子串的匹配中来

此时C与B不相等,它的上一个匹配的字符是A,它的next值为1,也就是说此时子串的第一个字符与第四个字符相等,子串的第四个字符与主串的第四个字符相等,所以子1与主4相等,可以直接跳过匹配,接下来应该匹配的是子串第二个字符B与主串第五个字符C。

理解kmp算法的一个要点是,主串是永远递增的,不会回退,子串匹配不相等时会回退。

如果看到这里你还是不太明白,可以结合下面的代码进行理解。

public int KMP(cusString S, cusString T, int pos) {
        int i = pos;
        int j = 0;
        int[] next = new int[T.getLength()];
        getNext(T, next);
        while (i < S.getLength() && j < T.getLength()) {
            if (S.chars[i] == T.chars[j]) {
                i++;
                j++;
            }else {
                if (j == 0) {
                    i++;
                }else {
                    j = next[j - 1];
                }
            }
        }
        if(j == T.getLength()) {
            return i - T.getLength();//起始位置索引
        }else {
            return -1;
        }

    }

public void getNext (cusString T,int[] next){
        next[0] = 0;
        int g = 0;
        int i = 1;
        while (i < T.getLength()) {
            if (T.chars[i] == T.chars[g]) {
                g++;
                next[i] = g;
                i++;
            } else {
                if (g == 0) {
                    next[i] = 0;
                    i++;
                } else {
                    g = next[g - 1];
                }
            }

        }
    }

下面是串的数据结构的定义

public class cusString {
    private int maxSize = 10;
    private char[] chars;
    private int length;

    public cusString() {
        chars = new char[maxSize];
        length = 0;
    }

    public cusString(int n) {
        if (n <= 0) {
            System.out.println("n must be > 0");
            System.exit(1);
        }
        chars = new char[n];
        length = 0;
    }
}

下面是一个测试的完整代码

public class KMPTest {

    public static void main(String[] args) {
        // 测试模式串
        cusString pattern = new cusString("ababaaaba");

        // 初始化一个数组用于存储计算得到的 next 数组
        int[] next = new int[pattern.getLength()];

        // 调用 getNext 方法计算 next 数组
        getNext(pattern, next);

        // 输出计算得到的 next 数组
        System.out.println("Next 数组: " + Arrays.toString(next));
    }

    // 这里简化了 cusString 类的定义,你需要确保 cusString 类中有 getLength 和 chars 属性的实现
    static class cusString {
        private final char[] chars;

        public cusString(String str) {
            this.chars = str.toCharArray();
        }

        public int getLength() {
            return chars.length;
        }
    }

    // getNext 方法的实现
    public static void getNext(cusString T, int[] next) {
        next[0] = 0;
        int g = 0;
        int i = 1;

        while (i < T.getLength()) {
            if (T.chars[i] == T.chars[g]) {
                g++;
                next[i] = g;
                i++;
            } else {
                if (g == 0) {
                    next[i] = 0;
                    i++;
                } else {
                    g = next[g - 1];
                }
            }
        }
    }
}

  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值