【算法】【算法杂谈】KMP算法:求字符串match在str中出现的第一个位置

目录

  • 前言
  • 问题介绍
  • 解决方案
  • 代码编写
    • java语言版本
    • c语言版本
    • c++语言版本
  • 思考感悟
  • 写在最后

前言

当前所有算法都使用测试用例运行过,但是不保证100%的测试用例,如果存在问题务必联系批评指正~

在此感谢左大神让我对算法有了新的感悟认识!

问题介绍

原问题
给定字符串str和字符串match,其中str字符串比match长,求match在str中出现的第一个字符的位置index是什么
时间复杂度控制在O(n),n是字符串str的长度

解决方案

原问题
要想将时间复杂度降低,则必须使用空间来加以辅助。这里介绍著名的kmp算法:
1、获取next数组,其中next[i]代表match[i]字符串前面的字符串中(不包含match[i])前缀与后缀相同的最长长度,通过循环获取(思考中会讲到算法)
2、获取到next数组,开始匹配match和str,申请两个游标i和j,i为str的游标,j为match的游标,当str[i] == match[j]时,匹配则两个游标同时往后走,如果j结束了,说明全匹配,则直接返回当前match的头结点所在str的位置即可。
3、如果str[i] != match[j]时,假设前面已经有k个值匹配了,则此时查看next[j]发现next[j] 为3,则说明match[j]前面的字符串中前缀和后缀最长匹配长度为3,j直接从3开始,i不再移动,继续从match[j]位置开始比较即可。直到结束,如果j没有到头,说明不匹配返回-1即可。

代码编写

java语言版本

原问题:
原问题:

  public class KMP {


    /**
     * 二轮测试:获取match字符串在str出现的第一个位置
     * @param str
     * @param match
     * @return
     */
    public static Integer getIndexFromStr(String str, String match) {
         if (str == null || match == null || str.length() == 0 || match.length() == 0) {
             return -1;
         }
        char[] ss = str.toCharArray();
        char[] ms = match.toCharArray();

        int[] nextArr = getNextArr(match);

        int sIndex = 0;
        int mIndex = 0;

        while (sIndex < ss.length && mIndex < ms.length) {
            if (ss[sIndex] == ms[mIndex]) {
                sIndex++;
                mIndex++;
            }else if (nextArr[mIndex] == -1) {
                // 说明比较的是开头
                sIndex++;
            }else {
                // sindex不变
                mIndex = nextArr[mIndex];
            }
        }
        return mIndex == ms.length ? sIndex - mIndex : -1;
    }

    private static int[] getNextArr(String match) {
        if (match.length() == 1) {
            return new int[]{-1};
        }
        char[] chars = match.toCharArray();
        int[] next = new int[chars.length];

        next[0] = -1;
        next[1] = 0;

        int pos = 2;
        int ci = 0;

        while (pos < next.length) {
            if (chars[pos-1] == chars[ci]) {
                // 将index作为长度set进去了,这里比较巧妙
                next[pos++] = ++ci;
            }else if (ci > 0){
                // 说明还没到chars[0]的位置,还可以继续循环
                ci = next[ci];
            }else {
                // 说明已经到头了,不存在前后缀相同的情况
                next[pos++] = 0;
            }
        }
        return next;
    }


    public static void main(String[] args) {
        System.out.println(getIndexFromStr("abaddf", "add"));
    }


}

c语言版本

正在学习中

c++语言版本

正在学习中

思考感悟

这里说一下nextarr的计算方法:
首先计算nextArr[i]时,我们先看nextArr[i-1],假如nextArr[i-1]为3,此时看match[3]位置是否和nextArr[i]相等,如果相等,则nextArr[i-1] + 1就是nextArr[i],如果不相等,则计算nextArr[nextArr[3]] 是否和nextArr[i]相等,相等则 nextArr[nextArr[3]] + 1 就是nextArr[i],如果不等则如此往复直到起点,则nextArr[i] = 0
算法有点复杂,不过通过图解得方式自己理解的话则非常简单

写在最后

方案和代码仅提供学习和思考使用,切勿随意滥用!如有错误和不合理的地方,务必批评指正~
如果需要git源码可邮件给[email protected]
再次感谢左大神对我算法的指点迷津!

你可能感兴趣的:(阅读随笔,算法,java专栏,算法,java)