【Lintcode】594. strStr II

题目地址:

https://www.lintcode.com/problem/strstr-ii/description

给定两个字符串source和target,寻找target在source中匹配的第一个位置的下标,如果找不到匹配则返回-1。

法1:KMP算法(未优化版)。解释起来非常费事,这里就省略了。代码如下:

public class Solution {
    /*
     * @param source: A source string
     * @param target: A target string
     * @return: An integer as index
     */
    public int strStr2(String source, String target) {
        // write your code here
        // 特判一下
        if (source == null || target == null) {
            return -1;
        }
        if (target.isEmpty()) {
            return 0;
        }
        
        // next[i]表示target[0,...,i - 1]中最长的相等真前缀和真后缀的长度,也表示当target[i]失配时,要接着从target的哪一个下标开始匹配。
        int[] next = buildNext(target);
        int i = 0, j = 0;
        while (i < source.length() && j < target.length()) {
            if (j < 0 || source.charAt(i) == target.charAt(j)) {
            	// 匹配,则继续看下一个字符
                i++;
                j++;
            } else {
            	// 否则,查表,找一下下一个”值得“匹配的字符
                j = next[j];
            }
        }
        
        return j == target.length() ? i - j : -1;
    }
    
    private int[] buildNext(String target) {
        int[] next = new int[target.length()];
        int t = next[0] = -1;
        int j = 0;
        // 这个循环实际上是在算next[j + 1]
        while (j < target.length() - 1) {
            if (t < 0 || target.charAt(j) == target.charAt(t)) {
                next[++j] = ++t;
            } else {
                t = next[t];
            }
        }
        
        return next;
    }
}

时间复杂度O(n+m)O(n+m)(buildNext需要O(m)O(m),考虑k=2ijk=2i-j,那么在while循环里,要么iijj同时增加11,要么jj被替换为next[j]next[j]从而kk也会增加。所以整个while循环里,kk是严格单调增加的。算法结束的时候,ii最大是nn,而jj最小是1-1,所以k=O(n)k=O(n)),空间O(m)O(m)nn为source的长度,mm为target长度。

法2:KMP算法(优化版)。代码如下:

public class Solution {
    /*
     * @param source: A source string
     * @param target: A target string
     * @return: An integer as index
     */
    public int strStr2(String source, String target) {
        // write your code here
        if (source == null || target == null) {
            return -1;
        }
        if (target.isEmpty()) {
            return 0;
        }
        
        int[] next = buildNext(target);
        int i = 0, j = 0;
        while (i < source.length() && j < target.length()) {
            if (j < 0 || source.charAt(i) == target.charAt(j)) {
                i++;
                j++;
            } else {
                j = next[j];
            }
        }
        
        return j == target.length() ? i - j : -1;
    }
    
    private int[] buildNext(String target) {
        int[] next = new int[target.length()];
        int t = next[0] = -1;
        int j = 0;
        while (j < target.length() - 1) {
            if (t < 0 || target.charAt(j) == target.charAt(t)) {
            	// 唯一优化的地方在这里。
            	// 当target[j] = target[t]的时候,我们已经知道target[j]和文本串不匹配了,
            	// 那再把target[t]拿过来匹配会是无用功,所以直接再跳一步
            	// 那为什么只需要再跳一步呢,这是因为在算next[t]的时候,
            	// 我们已经保证了target[t]和target[next[t]]是不等的了,所以不需要跳两步
                j++;
                t++;
                next[j] = target.charAt(j) != target.charAt(t) ? t : next[t];
            } else {
                t = next[t];
            }
        }
        
        return next;
    }
}

时空复杂度一样。

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