leetcode 30. Substring with Concatenation of All Words java代码,最快耗时13ms

原题链接:https://leetcode.com/problems/substring-with-concatenation-of-all-words/description/

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

 利用哈希表和滑动窗口可以以O(n)的时间复杂度解决问题。

思路:将words数组中的字符串存入hashmap,值为出现次数。然后开始遍历字符串s,外层循环从0到word[0].length-1,内层循环从i到最后(需判断最后是否越界),内层循环每次增加一个word的长度,这样外层循环完毕后刚好遍历完所有情况。(注意,words中的每一个word长度相同,所以才可以这样遍历)

维持一个滑动窗口,左右边界都只向右移动,每次先移动right,窗口大小表示匹配到的word个数,随着移动而增大或减小。移动right,如果map中存在当前字符串,修改map中对应word的个数,一旦发现当前需要修改的个数已经为0,说明出现了多余的字符串,窗口需要更新,这时候开始移动窗口左边界,直到左边界滑过与当前字符串相同的字符串,滑动左边界的同时把map中对应字符串的个数加回来。  如果当前map中找不到cur(当前字符串),说明cur之前的窗口已经没用了,重置窗口大小为0,将left到right的map值加回来,然后从下一个位置重新开始匹配。

当窗口大小与words长度相同时,说明s中从left到right+wLen这一部分刚好可以匹配words,将left存入list即可。

多说无益,代码如下: 

public List findSubstring(String s, String[] words) {
		List list = new ArrayList();
		if (s == null || s.length() == 0 || words == null || words.length == 0)
			return list;
		int sLen = s.length(), wLen = words[0].length(), wsLen = words.length;
		if (sLen < wLen * wsLen)
			return list;
		Map map = new HashMap<>(); // 记录words中每一个字符串的数目
		for (String word : words) {
			if (map.containsKey(word))
				map.put(word, map.get(word) + 1);
			else
				map.put(word, 1);
		}
		for (int i = 0; i < wLen; i++) {
			int left = i, right = i, window = 0; // 窗口的大小和左右边界
			while (right + (wsLen - window) * wLen <= sLen && right + wLen <= sLen) {
				String cur = s.substring(right, right + wLen); // right位置的字符串
				if (map.containsKey(cur)) {
					int cnt = map.get(cur); // 当前字符串的个数
					window++; // 包含当前字符串,窗口大小+1
					if (cnt > 0) {
						map.put(cur, cnt - 1);
					} else { // map当前字符串个数为0,说明出现重复字符串
						String removed = s.substring(left, left + wLen); // 从窗口左边开始移除字符串
						while (!removed.equals(cur)) {
							map.put(removed, map.get(removed) + 1); // 恢复移除字符串的个数
							left += wLen;
							window--;
							removed = s.substring(left, left + wLen);
						}
						left += wLen;
						window--;
					}
					if (window == wsLen) // 窗口大小等于数组长度,匹配成功
						list.add(left);
				} else {
					// 恢复map
					window = 0;
					while (left < right) {
						String removed = s.substring(left, left + wLen); // 从窗口左边开始移除字符串
						map.put(removed, map.get(removed) + 1); // 恢复移除字符串的个数
						left += wLen;
					}
					left += wLen; // 左边跳过当前这一位不匹配的字符串
				}
				right += wLen; // 窗口往右拓展一个字符串的长度
			}
			// 恢复map
			while (left < right) {
				String removed = s.substring(left, left + wLen); // 从窗口左边开始移除字符串
				map.put(removed, map.get(removed) + 1); // 恢复移除字符串的个数
				left += wLen;
			}
		}
		return list;
	}

 

你可能感兴趣的:(LeetCode)