KMP算法

本小白今天学习了KMP算法(太疲劳了,尽管脑壳疼也要把今天的学习任务记录下来应付秋招~ ),KMP算法名字的由来据说是三个人名,解决的是在字符串str1中匹配str2的问题。如果str1的某一子串为str2,则返回str2字符串的第一个字符在str1中角标,否则返回-1。

暴力解

这个问题简单粗暴的解法的就是从str1的第一个字符开始比对str2,若出现不符合的情况,则由str1下一个字符开始重新匹配,直至str1遍历结束(失败)或str2匹配成功(成功)。如果str1的长度为n,str2的长度为m,则时间复杂度为O(n * m)。这个方法在笔试时必定超时。
贴代码:

	public static int solution(String str1, String str2) {
        if (str1.length() == 0 || str2.length() == 0) return -1;
        for (int i = 0; i < str1.length(); i++) {
            int j = 0;
            for (; j < str2.length(); j++) {
                if (i + j >= str1.length() || str1.charAt(i + j) != str2.charAt(j)) {
                    break;
                }
            }
            if (j == str2.length()) {
                return i;
            }
        }
        return -1;
    }

KMP算法

这里就不分析原理了(实在懒于画图 ),具体的流程和原理在网上各位大佬的博文里都能找到。这里大致提一下流程,KMP算法中用到了一个辅助数组,个人认为也是空间换时间的经典案例了,KMP算法大致分为两步:

  1. 先求解辅助数组next[]数组;
  2. 再进行比对。

其中,next[]数组的长度和str2数组的长度一致,它的每个元素next[i]表示在str2[0]~str2[i - 1]中最大的 前缀字母和后缀字母相同的个数并且这个长度必须要小于i(这个一会儿解释),next[0]固定设置为-1。

举个栗子,str1 = “abcbcabc”,str2 = “bcbcb”;
next[0]:0这个固定设置为-1;
next[1]:“b”(str2[0 至 i] = “b”)的 最大前缀字母和后缀字母相同的个数并且这个长度必须要小于i,那么就是0了,因为"b"的第一个前缀为b,第一个后缀也为b,他们相同,长度为1,但要小于i(此时i = 1),所示只能为0;(为什么长度要小于i,因为str2[0]~str2[i - 1],它本身必然等于它本身,这没什么好记录的)
next[2]:"bc"的第一个前缀为b,第一个后缀为c,不相同,所以next[2] = 0;
next[3]:"bcb"的第一个前缀为b,第一个后缀为b,相同;前缀为2时为“bc”,后缀为2时为“cb”,也不相同,所以next[3] = 1;
next[4]:“bcbc”,next[4] = 2;
综上:next[] = {-1,0,0,1,2};

讲解完next数组的含义,开始讲解究竟如何实现创建next[]的过程。next[0] = -1; next[1] = 0, 这两种情况就不讲解了。我们看任意位置next[i]如何求解:

  1. 再此之前,先定义一个k(跳转位置);
  2. 如果str2[i - 1]的字符与跳转位置的字符str2[k]相同,则next[i] = k + 1;i ++;k ++;
  3. 如果str2[i - 1]的字符与str2[k]不相同,那么先令k = str2[k],继续比较str2[i - 1]与str2[k]是否相同,重复2、3步骤,直到它们相同或者k = -1。

贴代码:

	public static int[] getNext(String s) {
        int[] next = new int[s.length()];
        if (s.length() == 0) return next;
        next[0] = -1;
        if (s.length() == 1) return next;
        int k = -1;//跳转位置
        int j = 1;//计算位置
        while (j < s.length()) {
            if (k == -1 || s.charAt(j - 1) == s.charAt(k)) {
                next[j++] = ++k;
            } else {
                k = next[k];
            }
        }
        return next;
    }

求完next[]数组后就是比对的过程了。str1与str2比对的流程:

  1. 若str1[i] == str2[j],则i ++,j++;
  2. 若str1[i] != str2[j],则令j = next[j],str1[i]继续和str2[j]比对,若直到j = 0 即next[j] = -1时str1[i]和str2[j]还不相同,i++;
  3. 重复1,2的过程直至j = str2.length()(成功),或者j != str2.length() && i = str1.length()(失败)。

贴代码:

	public static int KMP(String s1, String s2) {
        if (s1.length() <= 0 || s2.length() <= 0 || s1.length() < s2.length()) return -1;
        int index1 = 0;
        int index2 = 0;
        int[] next = getNext(s2);
        while (index2 < s2.length() && index1 < s1.length()) {
            if (index2 == -1 || s1.charAt(index1) == s2.charAt(index2)) {
                index1++;
                index2++;
            } else {
                index2 = next[index2];
            }
        }
        return index2 == s2.length() ? index1 - index2 : -1;
    }

到此KMP算法结束了,写的比较简洁,没有贴图示意,主要还是为了记录本小白新学习的内容加强记忆~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值