![b0b3b194dd6f212c6fbde9284cc4acfd.png](https://i-blog.csdnimg.cn/blog_migrate/3f58823a23afdeb35346dd3c7cfc67f7.jpeg)
题目
给定一个字符串 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"]输出:[]
解题代码与测试
Created by tannzh on 2020/6/29.///* * 串联所有单词的子串 给定一个字符串 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"]输出:[] */#include #include #include #include using std::string;using std::vector;class Solution {public: vector findSubstring(string s, vector& words) { if (s.empty() || words.empty()) return {}; vector res; int n = words.size(), len = words[0].size(); std::unordered_map wordCnt; for (auto &word : words) ++wordCnt[word]; for (int i = 0; i <= (int)s.size() - n * len; ++i) { std::unordered_map strCnt; int j = 0; for (j = 0; j < n; ++j) { string t = s.substr(i + j * len, len); if (!wordCnt.count(t)) break; ++strCnt[t]; if (strCnt[t] > wordCnt[t]) break; } if (j == n) res.push_back(i); } return res; }};int main(int argc, char **argv){ std::string s1 = "barfoothefoobarman"; std::string s2 = "wordgoodgoodgoodbestword"; std::vector<:string> words1 = {"foo","bar"}; std::vector<:string> words2 = {"word","good","best","word"}; Solution s; auto res1 = s.findSubstring(s1, words1); auto res2 = s.findSubstring(s2, words2); for(auto n : res1) { std::cout << n << " "; } std::cout << std::endl; for(auto n : res2) { std::cout << n << " "; } std::cout << std::endl; return 0;}
解题思路分析
求串联所有单词的子串,就是说给定一个长字符串,再给定几个长度相同的单词,让找出串联给定所有单词的子串的起始位置,还是蛮有难度的一道题。假设 words 数组中有n个单词,每个单词的长度均为 len,那么实际上这道题就让我们出所有长度为 nxlen 的子串,使得其刚好是由 words 数组中的所有单词组成。那么就需要经常判断s串中长度为 len 的子串是否是 words 中的单词,为了快速的判断,可以使用 HashMap,同时由于 words 数组可能有重复单词,就要用 HashMap 来建立所有的单词和其出现次数之间的映射,即统计每个单词出现的次数。
遍历s中所有长度为 nxlen 的子串,当剩余子串的长度小于 nxlen 时,就不用再判断了。所以i从0开始,到 (int)s.size() - nxlen 结束就可以了,注意这里一定要将 s.size() 先转为整型数,再进行解法。一定要形成这样的习惯,一旦 size() 后面要减去数字时,先转为 int 型,因为 size() 的返回值是无符号型,一旦减去一个比自己大的数字,则会出错。对于每个遍历到的长度为 nxlen 的子串,需要验证其是否刚好由 words 中所有的单词构成,检查方法就是每次取长度为 len 的子串,看其是否是 words 中的单词。为了方便比较,建立另一个 HashMap,当取出的单词不在 words 中,直接 break 掉,否则就将其在新的 HashMap 中的映射值加1,还要检测若其映射值超过原 HashMap 中的映射值,也 break 掉,因为就算当前单词在 words 中,但若其出现的次数超过 words 中的次数,还是不合题意的。在 for 循环外面,若j正好等于n,说明检测的n个长度为 len 的子串都是 words 中的单词,并且刚好构成了 words,则将当前位置i加入结果 res 即可。
具体看代码实现,有兴趣的可以自己调试一下。