【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;
    }
}

時空複雜度一樣。

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