KMP算法(LeetCode.796)

LeetCode.796:力扣

        这个题虽然被力扣规划为简单难度,是因为它下限低,可以依赖String类的stratWith()方法,很轻松解决,但是用stratWith()方法学不到什么东西,最好可以用这个题练一下自己对KMP算法的掌握程度。

        思路:这个题意思就是经过若干次旋转得到goal目标字符串。首先如果两个字符串长度不相等(长度一大一小),再怎么旋转都得不到goal字符串。然后骚的点来了:将原来的s字符串变成两倍,若是在里面能够找到goal字符串,那么就可以通过旋转得到goal字符串。

        分析:为什么?为什么将原来的s字符串扩大两倍(eg:“abc” ---> "abcabc"),这样在里面寻找目标字符串就行了。因为将原来的s字符串扩大两倍之后,不管旋转多少次的字符串,都是两倍字符串的字串。(eg:“abc” ---> "abcabc",goal:“cab”。"abcabc"由0位置开始找三个是“abc”、由1位置开始找三个是“bca”、由2位置找三个是“cab”、由3位置找三个是“abc”,出现循环了)。将原来的字符串扩大两倍的字符串包含目标字符串的所有旋转结果。

方法一:投机取巧利用startWhit()方法

        遍历两倍的那个字符串,以每一个位置作为开头,判断是否有目标字符串。

    public static boolean rotateString(String s, String goal) {
        if (s.length() != goal.length()) {
            return false;
        }
        String string = s + s;
        for (int i = 0; i < string.length(); i++) {
            if (string.startsWith(goal, i)) {
                return true;
            }
        }
        return false;
    }

方法二:KMP算法(重点)

         KMP算法的大思路也是遍历每一个位置,但是有一个小加速过程。

        next数组:next数组和原string字符串等长,每一个位置的含义:当前位置字符之前,最大的前缀==最大的后缀,next[i]==这个最大的前(后)缀字符数,(eg:“ababc”这个字符串,对c位置来说,前缀和后缀有很多,前缀“a”、“ab”、“aba”;后缀:“b”、“ab”、“bab”,最大前缀“ab”,最大后缀“ab”,为什么是“ab”而不是“aba”或者“bab”?因为这两个前缀和后缀根本不相等,需要前缀==后缀,那么对于c位置(i==4位置)来说,next[i] ==2)。这个就是next数组的含义。

        next数组的生成:

    public static int[] getNextArr(char[] chars) {
        if (chars.length == 1) {
            return new int[]{-1};
        }
        int[] next = new int[chars.length];
        next[0] = -1;//next数组前两个位置的值分别是-1,0,没有例外
        int i = 2;//i的值表示该在哪个位置求next数组的值(遍历chars)
        int cn = 0;//cn代表哪个位置该和i-1相比较
        while (i < chars.length) {
            if (chars[i - 1] == chars[cn]) {
                next[i++] = ++cn;
            } else if (cn > 0) {//cn还没有等于0,还能往左移,然后去匹配
                cn = next[cn];
            } else {//cn不能左移了,而且当前位置没有和之前的next[cn]匹配成功
                next[i++] = 0;
            }
        }
        return next;
    }

 有了next数组,接下来实现KMP算法:

        假设string字符串到了i位置开始和目标字符串进行匹配,string从i位置开始、goal从0位置开始,直到遇到一个位置(此时string来到x位置、goal来到y位置),二者的字符不相等。因为有next数组,所以我们知道:next[y],即y位置之前的最大前缀和后缀(前缀==后缀),那么将goal向后推,让string的x位置和goal的next[y]位置进行比较,若相等,则两个位置都加一,继续匹配,不相等则x位置和goal的next[next[y]]位置进行比较,直到匹配成功可以继续匹配,或者next数组来到了0位置,此时说明当前string 的x位置不能匹配出一个完整的goal,则string去往下一个位置(即i+1)的位置,以此类推。

实现KMP算法的代码:

    public static int KMP(String string, String goal) {
        char[] str1 = string.toCharArray();
        char[] str2 = goal.toCharArray();
        int x = 0;//string字符串该哪个位置开始比对了
        int y = 0;
        int[] next = getNextArr(str2);
        while (x < str1.length && y < str2.length) {
            if (str1[x] == str2[y]) {
                x++;
                y++;
            } else if (next[y] == -1) {
                x++;//去string字符串中下一个该匹配的位置开始比对
            } else {
                y = next[y];//y缩小开始比对
            }
        }
        //如果是因为str1越界退出的while循环,那么说明尝试了所有的str1位置都不能匹配出完整的str2
        return y == str2.length ? x - y : -1;
    }

 力扣796题KMP算法提交结果:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值