【LeetCode】30. Substring with Concatenation of All Words

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: []

 

Solution: Mark the occurence of each word in words into a hashmap, for each i, keep checking the following len(word)'s string word occured or not, if no , go for next i(Notice that substring, exactly once), if yes, decrease occurence of the string word 1, when occurence of all words is 0, then add current i to result list.

Key point:

1. end point of i

2. end point of j

 

Code: 170 ms, Time complexity O(N * k), N is the length of given string s; k is the length of words.

public List<Integer> findSubstring(String s, String[] words) {
        List<Integer> res = new ArrayList<>();
        if (s == null || s.length() == 0 || words == null || words.length == 0) {
            return res;
        }
        // mark occurence of each word in words
        Map<String, Integer> hashMap = new HashMap<>();
        for (String ss : words) {
            hashMap.put(ss, hashMap.getOrDefault(ss, 0) + 1);
        }
        
        int len = words[0].length();
        for (int i = 0; i <= s.length() - len * words.length; i++) {
            Map<String, Integer> map = new HashMap<>(hashMap);
            int j = i;
            while (j + len <= s.length()) {
                String cur = s.substring(j, j + len);
                if (!map.containsKey(cur)) {
                    break;
                } else {
                    j += len;
                    map.put(cur, map.get(cur) - 1);
                    if (map.get(cur) == 0) {
                        map.remove(cur);
                    }
                    if (map.isEmpty()){
                        res.add(i);
                    }
                }
            }
            
        }
        return res;
    }

 

Solution from leetcode discuss:

There could be several starting index i in string s which meet the needs. We can find these substrings by maintaining sliding window. Match the words in substring with given input words array. Iterate by words instead of by character. 

To be more effective, in the example, given string s has length 18, word in words has length 3. So first time we cut s into new words with each having index 0, 3, 6, 9, 12, 15. Second time, we cut s into new words with each having index 1, 4, 7, 10, 13, 16. Third time, we cut s into new words with each having index 2, 5, 8, 11, 14, 17. Then we cover every cases. Each time we have a list of new words, then we maintain a substring by using a sliding window --- add word into right side of sliding window when we don't have enough words in substring conpared with number of words in given words array, --- remove left side of word when we have extra word in substring. Every time after adding word or removing word, check if current match given words array. So we need to pre compute the number of distinct words, if the occurrence time of a word equals to occurence in given words, then matched word count++. If count == number of distinct words in given words array, then i --- left side of sliding window --- is added in result list. Time complexity: we have len times loop, each loop, we have N / len words, a word is visited by at most 2 times. So len * 2 * N/len = O(N), N is the length of given string s.

 

Code: 22 ms; time complexity O(n)

public List<Integer> findSubstring(String s, String[] words) {
        List<Integer> res = new ArrayList<>();
        if (words == null || words.length == 0) {
            return res;
        }
        
        int distinctWordCount = 0;
        Map<String, Integer> wordDict = new HashMap<>();
        for (String word : words) {
            wordDict.put(word, wordDict.getOrDefault(word, 0) + 1);
            if (wordDict.get(word) == 1) {
                distinctWordCount++;
            }
        }
        
        int len = words[0].length();
        for (int k = 0; k < len; k++) {
            Map<String, Integer> map = new HashMap<>();
            int count = 0;
            for (int i = k, j = k; j <= s.length() - len; j += len){
                if ((j - i) / len >= words.length) {
                    String rmWord = s.substring(i, i + len);
                    if (wordDict.containsKey(rmWord)) {
                        map.put(rmWord, map.get(rmWord) - 1);
                        if (map.get(rmWord) == wordDict.get(rmWord) - 1) {
                            count--;
                        }
                    }
                    i += len;
                }
                
                String addWord = s.substring(j, j + len);
                if (wordDict.containsKey(addWord)) {
                    map.put(addWord, map.getOrDefault(addWord, 0) + 1);
                    if (map.get(addWord) == wordDict.get(addWord)) {
                        count++;
                    }
                }
                if (count == distinctWordCount) {
                    res.add(i);
                }
            }
        }
        return res;
    }

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值