1. 程式人生 > >【LeetCode】30. Substring with Concatenation of All Words - Java實現

【LeetCode】30. Substring with Concatenation of All Words - Java實現

文章目錄

1. 題目描述:

You are given a string, s, and a list of words, words, that are all of the same length. Find all starting indices of substring(s) in s that is a concatenation of each word in words exactly once and without any intervening characters.

Example 1:

Input:
s = “barfoothefoobarman”,
words = [“foo”,“bar”]
Output: [0,9]
Explanation: Substrings starting at index 0 and 9 are “barfoor” and “foobar” respectively.
The output order does not matter, returning [9,0] is fine too.

Example 2:

Input:
s = “wordgoodstudentgoodword”,
words = [“word”,“student”]
Output: []

2. 思路分析:

題目的意思是給定一個字串和一個包含若干個詞的列表,然後找出列表中所有詞的各種組合在字串中的位置。

由於陣列中所有單詞的長度都是一樣的,我們可以像3.Longest Substring with At Most Two Distinct Characters中一樣,把每個詞當作一個字母來看待,但是要遍歷K次,K是單詞的長度,因為我們要分別統計從下標0開頭,從下標1開頭。。。直到下標K-1開頭的字串。舉例來說foobarfoo,給定陣列是[foo, bar],那我們要對foo|bar|foo搜尋一次,對oob|arf|oo搜尋一次,對oba|rfo|o搜尋一次,我們不用再對bar|foo搜尋,因為其已經包含在第一種裡面了。每次搜尋中,我們通過雜湊表維護一個視窗,比如foo|bar|foo中,我們先拿出foo。如果foo都不在陣列中,那說明根本不能拼進去,則雜湊表全部清零,從下一個詞開始重新匹配。但是foo是在陣列中的,所以給當前搜尋的雜湊表計數器加上1,如果發現當前搜尋中foo出現的次數已經比給定陣列中foo出現的次數多了,我們就要把上一次出現foo之前的所有詞都從視窗中去掉,如果沒有更多,則看下一個詞bar,不過在這之前,我們還要看看視窗中有多少個詞了,如果詞的個數等於陣列中詞的個數,說明我們找到了一個結果。

3. Java程式碼:

原始碼見我GiHub主頁

程式碼:

public static List<Integer> findSubstring(String s, String[] words) {
    List<Integer> result = new ArrayList<>();
    if (s == null || s.length() == 0 || words == null || words.length == 0) {
        return result;
    }

    // 用於記錄詞表中每個詞的數目
    Map<String, Integer> wordCount = new HashMap<>();
    for (String word : words) {
        int cnt = wordCount.getOrDefault(word, 0);
        wordCount.put(word, cnt + 1);
    }

    // 用於記錄每個詞的長度,所有詞長度一樣
    int len = words[0].length();

    for (int i = 0; i < len; i++) {
        // 用於記錄當前已匹配到詞表中的各個詞的個數, 當作一個滑動視窗的作用
        Map<String, Integer> curWordCount = new HashMap<>();
        // 記錄移動視窗的起始位置
        int start = i;
        // 記錄已匹配詞的個數
        int count = 0;
        for (int j = i; j <= s.length() - len; j += len) {
            String curWord = s.substring(j, j + len);

            // 表示當前詞不在詞表中,清空視窗,重新開始
            if (!wordCount.containsKey(curWord)) {
                curWordCount.clear();
                count = 0;
                start = j + len;
                continue;
            }

            int curCnt = curWordCount.getOrDefault(curWord, 0);
            curWordCount.put(curWord, curCnt + 1);
            count++;

            // 如果當前的詞多匹配了,此時需要不斷將最左邊的詞清理出去,直到當前詞的數目滿足條件
            while (curWordCount.get(curWord) > wordCount.get(curWord)) {
                String leftWord = s.substring(start, start + len);
                curWordCount.put(leftWord, curWordCount.get(leftWord) - 1);
                count--;
                start += len;
            }

            // 表示一次匹配成功,存入結果中
            if (count == words.length) {
                result.add(start);
                String lefWord = s.substring(start, start + len);
                curWordCount.put(lefWord, curWordCount.get(lefWord) - 1);
                count--;
                start += len;
            }
        }
    }
    return result;
}