【Leetcode】28. Implement strStr()

题目地址:

https://leetcode.com/problems/implement-strstr/

给定两个字符串,判断第二个是否是第一个的子串,如果是则返回匹配的起始地址,否则返回1-1

法1:暴力法。直接枚举所有的开始位置,依次做匹配,一旦匹配成功则返回起始点下标,如果一直未成功则返回1-1。代码如下:

public class Solution {
    public int strStr(String s, String p) {
        if (s == null || p == null) {
            return -1;
        }
        
        if (p.isEmpty()) {
            return 0;
        }
    	
    	// i后面要留足p的长度这么多位置
        for (int i = 0; i + p.length() - 1 < s.length(); i++) {
        	// 开始从p[0]开始匹配
            int idx = 0;
            while (idx < p.length()) {
                if (s.charAt(i + idx) != p.charAt(idx)) {
                    break;
                }
                idx++;
            }
            
            // 匹配完了说明找到了子串,返回起始点下标
            if (idx == p.length()) {
                return i;
            }
        }
        
        return -1;
    }
}

时间复杂度O(nm)O(nm),空间O(1)O(1)

法2:KMP。KMP算法有一个next数组的概念,首先参考https://blog.csdn.net/qq_46105170/article/details/106168535,在未改进版本的KMP中,pp的next数组n[i]n[i]表示p[0:i1]p[0:i-1]中最长的相等前后缀的长度,而在改进版本中,n[i]n[i]则表示当p[i]p[i]s[j]s[j]不匹配的时候,pp里下一个要与s[j]s[j]匹配的字符的下标。未改进版本的KMP中,这个下标取的是最长的相等前后缀的长度,利用这个信息去移动pp,可以在不错过可能解的情况下,保证移动后和s[j]s[j]比较时,之前的字符已经全部相等,也就是不用再进行匹配了;但是它还可以利用一个信息,若移动后与s[j]s[j]对齐的那个字符仍然与p[i]p[i]相等,那么肯定会出错,还是得继续向后移,改进的KMP算法就是将这个信息加入进去。首先,若s[j]p[i]s[j]\ne p[i],那么下一个与s[j]s[j]对齐的字符变为了p[n[i]]p[n[i]],若还不等,则对齐的是p[n[n[i]]]p[n[n[i]]],直到相等或者变为1-1为止。如果我们已经知道了p[i]=p[n[i]]p[i]=p[n[i]],那再次对齐就没有意义,因为还要取一次next。所以索性在一开始就”next到底“,杜绝p[i]=p[n[i]]p[i]=p[n[i]]的情况发生。代码如下:

public class Solution {
    public int strStr(String s, String p) {
        if (s == null || p == null) {
            return -1;
        }
        
        if (p.isEmpty()) {
            return 0;
        }
        
        int i = 0, j = 0;
        int[] next = buildNext(p);
        while (i < s.length() && j < p.length()) {
            if (j == -1 || s.charAt(i) == p.charAt(j)) {
                i++;
                j++;
            } else {
                j = next[j];
            }
        }
        
        return j == p.length() ? i - j : -1;
    }
    
    private int[] buildNext(String p) {
        int[] next = new int[p.length()];
        int i = 0, j = next[0] = -1;
        while (i < p.length() - 1) {
            if (j == -1 || p.charAt(i) == p.charAt(j)) {
                i++;
                j++;
                // 在未改进的KMP算法中,next[i]直接取j;
                // 在改进的算法中需要判断一下当前字符是否和p[j]相等,如果相等则还需要next一下
                next[i] = p.charAt(i) == p.charAt(j) ? next[j] : j;
            } else {
                j = next[j];
            }
        }
        
        return next;
    }
}

时间复杂度O(n+m)O(n+m),空间O(m)O(m)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章