LeetCode 30.串联所有单词的子串

题目:

给定一个字符串 s 和一些 长度相同 的单词 words 。找出 s 中恰好可以由 words 中所有单词串联形成的子串的起始位置。

注意子串要与 words 中的单词完全匹配,中间不能有其他字符 ,但不需要考虑 words 中单词串联的顺序。

示例 1:

输入:s = "barfoothefoobarman", words = ["foo","bar"]
输出:[0,9]
解释:
从索引 0 和 9 开始的子串分别是 "barfoo" 和 "foobar" 。
输出的顺序不重要, [9,0] 也是有效答案。


示例 2:

输入:s = "wordgoodgoodgoodbestword", words = ["word","good","best","word"]
输出:[]


示例 3:

输入:s = "barfoofoobarthefoobarman", words = ["bar","foo","the"]
输出:[6,9,12]
 

提示:

1 <= s.length <= 104
s 由小写英文字母组成
1 <= words.length <= 5000
1 <= words[i].length <= 30
words[i] 由小写英文字母组成

思路:

由于单词长度相同,利用滑动窗口,每次滑动一个单词的长度,并且计数。由于主串不一定从索引为0开始匹配,窗口初始化的次数也为一个单词的长度。放上代码来理解:

class Solution {
public:
	vector findSubstring(string s, vector& words) {
		//特殊情况直接排除
		if (s.empty() || words.empty())return{};
		//存放结果的数组
		vectorresult;

		//一个单词的长度、words中的单词数量、总长度
		int one_word = words[0].size();
		int word_num = words.size();
		int all_len = one_word * word_num;

		//建立单词->单词个数得映射
		unordered_mapm1;
		for (const auto& w : words)m1[w]++;
        
        //窗口初始化的次数为one_word
		for (int i = 0; i < one_word; i++) {
			//窗口的左右边界,匹配得单词总数
			int left = i, right = i, count = 0;

			unordered_mapm2;
			
			//开始滑动窗口
			while (right + one_word <= s.size()) {
				//s中提取一个单词到w
				string w = s.substr(right, one_word);
				right += one_word;//右边界右移
				
				//单词不在m1中匹配不成功,重置边界、单词个数、m2
				if (m1.count(w) == 0) {
					count = 0;
					left = right;
					m2.clear();
				}
				//匹配成功,添加到m2
				else {
					m2[w]++;
					count++;
					//匹配次数多了,左边界left右移
					while (m2.at(w) > m1.at(w)) {
						string t_w = s.substr(left, one_word);
						count--;
						m2[t_w]--;
						left += one_word;
					}
					if (count == word_num)result.push_back(left);
				}
			}
		}
		return result;
	}
};

你可能感兴趣的:(算法,leetcode)