LeetCode 打卡day09 -- KMP算法

一个人的朝圣 — LeetCode打卡第9天

  • 知识总结
  • Leetcode 28. 找出字符串中第一个匹配项的下标
    • 题目说明
    • 代码说明
  • Leetcode 459. 重复的子字符串
    • 题目说明
    • 代码说明


知识总结

今天只有两道题, 而且是简单题, 但是一点都不简单. 因为我们学习了一种KMP算法, 而且需要掌握这个模板。


Leetcode 28. 找出字符串中第一个匹配项的下标

题目链接

题目说明

给你两个字符串 haystack 和 needle ,请你在 haystack 字符串中找出 needle 字符串的第一个匹配项的下标(下标从 0 开始)。如果 needle 不是 haystack 的一部分,则返回 -1 。

代码说明

方法一, 使用Java内置函数

class Solution {
    public int strStr(String haystack, String needle) {
        return haystack.indexOf(needle);
    }
}

方法二, 暴力循环法
时间复杂度 O ( M ∗ N ) O(M *N) O(MN)

class Solution {
    public int strStr(String haystack, String needle) {
        int hLen = haystack.length(), neLen = needle.length(); 
        for(int i = 0; i<= hLen - neLen; i++){
            if(needle.charAt(0) == haystack.charAt(i)){
                int left = 0;
                int right = i;
                while(left < neLen && needle.charAt(left) == haystack.charAt(right)){
                    left++;
                    right++;
                }
                if(left == needle.length()){
                    return i;
                }
            }
        }
        return -1;
    }
}

方法三,使用KMP方法
时间复杂度 O ( M + N ) O (M + N) O(M+N)
第一步: 用一个Next数组来记录最长公共前后缀的长度
如何理解这个最长公共前后缀?

String s = “aabaa”
它的前缀包括(所有包含头部不包含尾部的子串): a, aa, aab, aaba
他的后缀包括(所有包含尾部不包含头部的子串): a, aa, baa, abaa (注意顺序都是从左到右, 不是回文串)
最长的公共前后缀为 “aa”, 长度为2

那么 字符串 S = “aabaaf”
对应的Next 数组为 [0, 1, 0, 1, 2, 0]

第二步, 运用Next来查询, 可以当作模板来记住, 中间还用了另外一种KMP的方法,但是没有这个好理解。

class Solution {
    public int strStr(String haystack, String needle) {
        if(needle.isEmpty()) return 0;

        // get next array
        int[] next = new int[needle.length()];
        int j = 0;
        next[0] = 0;

        for(int i = 1; i < needle.length(); i++){
            while(j>0 && needle.charAt(i) != needle.charAt(j)){
                j = next[j-1];
            }
            if(needle.charAt(i) == needle.charAt(j)){
                j++;
            }
            // eventually j will equal to 0
            next[i] = j;
        }

        //from next array to match haystack
        j = 0;
        for(int i = 0; i< haystack.length(); i++){
            while(j > 0 && haystack.charAt(i) != needle.charAt(j)){
                j = next[j-1];
            }
            if(haystack.charAt(i) == needle.charAt(j)){
                j++;
            }

            if(j == needle.length()){
                return i - needle.length() + 1;
            }
        }
        return -1;
    }
}

方法四
KMP变种

class Solution {
    public int strStr(String haystack, String needle) {
        if(needle.isEmpty()) return 0;

        int[] LPS = new int[needle.length()];
        int prevLPS = 0, i = 1;

        // create the Longest prefix suffix array
        while(i < needle.length()){
            if(needle.charAt(i) == needle.charAt(prevLPS)){
                LPS[i] = prevLPS + 1;
                prevLPS++;
                i++;
            }else if(prevLPS == 0){
                LPS[i] = 0;
                i++;
            }else{
                prevLPS = LPS[prevLPS -1];
            }
        }

        i = 0; // pointer for haystack
        int j = 0; // pointer for needle
        while(i < haystack.length()){
            if(haystack.charAt(i) == needle.charAt(j)){
                i++;
                j++;
            }else{
                if(j == 0){
                    i ++;
                }else{
                    j = LPS[j -1];
                }
            }
            if(j == needle.length()){
                return i - needle.length();
            }
        }
        return -1;


    }
}

Leetcode 459. 重复的子字符串

题目链接

题目说明

给定一个非空的字符串 s ,检查是否可以通过由它的一个子串重复多次构成。

代码说明

方法一
暴力循环法,时间复杂度 O ( N 2 ) O(N^2) O(N2)

class Solution {
    public boolean repeatedSubstringPattern(String s) {
        int len = s.length();
        for(int i =1; i <= len / 2; i++){
            if(len % i != 0){
                continue;
            }
            String part = s.substring(0, i);
            for(int j = i; j<=len -i; j+= i){
                String next = s.substring(j, j+i);
                if(!next.equals(part)){
                    break;
                }
                if(j + i == len){
                    return true;
                }
            }
        }
        return false;
    }
}

方法二
使用KMP中的Next数组, 如果一个字符串由相同的重复的子字符串组成, 那么他们的最大的公共前后缀和原数组相差的,一定就是重复的字符串。
比如 S = “abcabc" Next[] = [0, 0, 0, 1, 2, 3]
最大公共前后缀为 abc, 长度为3.

6 % ( 6 - 3) = 0

所以 len % (len - 最大的公共前后缀长度) = 0
满足该条件说明是由重复字符串组成, 否则则不是

class Solution {
    public boolean repeatedSubstringPattern(String s) {
        int[] next = new int[s.length()];
        int j = 0;
        next[0] = 0;
        for(int i = 1; i<s.length(); i++){
            while(j>0 && s.charAt(i) != s.charAt(j)){
                j = next[j-1];
            }
            if(s.charAt(j) == s.charAt(i)){
                j++;
            }
            next[i] = j;
        }
        int len = s.length();
        if(next[len -1] != 0 && len % (len - next[len -1]) == 0){
            return true;
        }else{
            return false;
        }
    }
}

你可能感兴趣的:(leetcode,算法,java)