Hard!

题目描述:

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

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

示例 1:

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

示例 2:

输入:
s = "wordgoodstudentgoodword",
words = ["word","student"]
输出: []

解题思路:

这道题让我们求串联所有单词的子串,就是说给定一个长字符串,再给定几个长度相同的单词,让我们找出串联给定所有单词的子串的起始位置,还是蛮有难度的一道题。这道题我们需要用到两个哈希表,第一个哈希表先把所有的单词存进去,然后从开头开始一个个遍历,停止条件为当剩余字符个数小于单词集里所有字符的长度。这时候我们需要定义第二个哈希表,然后每次找出给定单词长度的子串,看其是否在第一个哈希表里,如果没有,则break,如果有,则加入第二个哈希表,但相同的词只能出现一次,如果多了,也break。如果正好匹配完给定单词集里所有的单词,则把i存入结果中,具体参见代码如下:

C++解法一:

 class Solution {
public:
vector<int> findSubstring(string s, vector<string>& words) {
vector<int> res;
if (s.empty() || words.empty()) return res;
int n = words.size(), m = words[].size();
unordered_map<string, int> m1;
for (auto &a : words) ++m1[a];
for (int i = ; i <= (int)s.size() - n * m; ++i) {
unordered_map<string, int> m2;
int j = ;
for (j = ; j < n; ++j) {
string t = s.substr(i + j * m, m);
if (m1.find(t) == m1.end()) break;
++m2[t];
if (m2[t] > m1[t]) break;
}
if (j == n) res.push_back(i);
}
return res;
}
};

这道题还有一种O(n)时间复杂度的解法,设计思路非常巧妙,但是感觉很难想出来。

这种方法不再是一个字符一个字符的遍历,而是一个词一个词的遍历,比如根据题目中的例子,字符串s的长度n为18,words数组中有两个单词(cnt=2),每个单词的长度len均为3,那么遍历的顺序为0,3,6,8,12,15,然后偏移一个字符1,4,7,9,13,16,然后再偏移一个字符2,5,8,10,14,17,这样就可以把所有情况都遍历到。

还是先用一个哈希表m1来记录words里的所有词,然后我们从0开始遍历,用left来记录左边界的位置,count表示当前已经匹配的单词的个数。然后我们一个单词一个单词的遍历,如果当前遍历的到的单词t在m1中存在,那么我们将其加入另一个哈希表m2中,如果在m2中个数小于等于m1中的个数,那么我们count自增1,如果大于了,那么需要做一些处理,比如下面这种情况, s = barfoofoo, words = {bar, foo, abc}, 我们给words中新加了一个abc,目的是为了遍历到barfoo不会停止,那么当遍历到第二foo的时候, m2[foo]=2, 而此时m1[foo]=1,这时候已经不连续了,所以我们要移动左边界left的位置,我们先把第一个词t1=bar取出来,然后将m2[t1]自减1,如果此时m2[t1]<m1[t1]了,说明一个匹配没了,那么对应的count也要自减1,然后左边界加上个len,这样就可以了。

如果某个时刻count和cnt相等了,说明我们成功匹配了一个位置,那么将当前左边界left存入结果res中,此时去掉最左边的一个词,同时count自减1,左边界右移len,继续匹配。如果我们匹配到一个不在m1中的词,那么说明跟前面已经断开了,我们重置m2,count为0,左边界left移到j+len

C++解法二:

 class Solution {
public:
vector<int> findSubstring(string s, vector<string>& words) {
if (s.empty() || words.empty()) return {};
vector<int> res;
int n = s.size(), cnt = words.size(), len = words[].size();
unordered_map<string, int> m1;
for (string w : words) ++m1[w];
for (int i = ; i < len; ++i) {
int left = i, count = ;
unordered_map<string, int> m2;
for (int j = i; j <= n - len; j += len) {
string t = s.substr(j, len);
if (m1.count(t)) {
++m2[t];
if (m2[t] <= m1[t]) {
++count;
} else {
while (m2[t] > m1[t]) {
string t1 = s.substr(left, len);
--m2[t1];
if (m2[t1] < m1[t1]) --count;
left += len;
}
}
if (count == cnt) {
res.push_back(left);
--m2[s.substr(left, len)];
--count;
left += len;
}
} else {
m2.clear();
count = ;
left = j + len;
}
}
}
return res;
}
};

LeetCode(30):与所有单词相关联的字串的更多相关文章

  1. [leetcode] 30. 与所有单词相关联的字串(cn第653位做出此题的人~)

    30. 与所有单词相关联的字串 这个题做了大概两个小时左右把...严重怀疑leetcode的judge机器有问题.同样的代码交出来不同的运行时长,能不能A题还得看运气? 大致思路是,给words生成一 ...

  2. Leetcode——30.与所有单词相关联的字串【##】

    @author: ZZQ @software: PyCharm @file: leetcode30_findSubstring.py @time: 2018/11/20 19:14 题目要求: 给定一 ...

  3. 30. 与所有单词相关联的字串、java实现

    题目描述: 给定一个字符串 s 和一些长度相同的单词 words.在 s 中找出可以恰好串联 words 中所有单词的子串的起始位置. 注意子串要与 words 中的单词完全匹配,中间不能有其他字符, ...

  4. Leetcode 30.与所有单词相关联的子串

    与所有单词相关联的字串 给定一个字符串 s 和一些长度相同的单词 words.在 s 中找出可以恰好串联 words 中所有单词的子串的起始位置. 注意子串要与 words 中的单词完全匹配,中间不能 ...

  5. [Swift]LeetCode30. 与所有单词相关联的字串 | 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 sta ...

  6. 030 Substring with Concatenation of All Words 与所有单词相关联的字串

    给定一个字符串 s 和一些长度相同的单词 words,找出 s 与 words 中所有单词(words 每个单词只出现一次)串联一起(words中组成串联串的单词的顺序随意)的字符串匹配的所有起始索引 ...

  7. Java实现 LeetCode 30 串联所有单词的子串

    30. 串联所有单词的子串 给定一个字符串 s 和一些长度相同的单词 words.找出 s 中恰好可以由 words 中所有单词串联形成的子串的起始位置. 注意子串要与 words 中的单词完全匹配, ...

  8. [LeetCode] 30. 串联所有单词的子串

    题目链接: https://leetcode-cn.com/problems/substring-with-concatenation-of-all-words/ 题目描述: 给定一个字符串 s 和一 ...

  9. Leetcode 30 串联所有单词的子串 滑动窗口+map

    见注释.滑动窗口还是好用. class Solution { public: vector<int> findSubstring(string s, vector<string> ...

随机推荐

  1. 强大的svg操作库——Raphael

    先常规先引入Raphael库: <script src="raphael.js" type="text/javascript"></scrip ...

  2. java基础-Idea开发工具介绍

    java基础-Idea开发工具介绍 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 之前给大家介绍过一款Java的IDE叫eclipse,有些功能用起来不是很得心应手,尤其是在导报的 ...

  3. iptables 代理转发配置

    增加命令 [root@pre /]# iptables -t nat -A PREROUTING -s 39.15.15.15/32 -d 221.11.8.200/32 -p tcp -m tcp ...

  4. 函数和常用模块【day06】:subprocess模块(十)

    本节内容 1.概述 2.前言 3.subprocess模块 4.subprocess.Popen() 一.概述 我们在实际的工作中,需要跟操作系统的命令做交互,但我们如何用python去跟操作系统之间 ...

  5. Spark记录-SparkSQL远程操作MySQL和ORACLE

    1.项目引入mysql和oracle驱动 2.将mysql和oracle驱动上传到hdfs 3.远程调试源代码如下: import org.apache.spark.sql.SQLContext im ...

  6. Hive记录-Hive常用命令操作

    1.hive支持四种数据模型 • external table ---外部表:Hive中的外部表和表很类似,但是其数据不是放在自己表所属的目录中,而是存放到别处,这样的好处是如果你要删除这个外部表,该 ...

  7. Java POI 读取word文件

    Apache POI是Apache软件基金会的开放源码函式库,POI提供API给Java程序对Microsoft Office格式档案读和写的功能. 1.读取word 2003及word 2007需要 ...

  8. Java入门系列(十)Java IO

    概述 总体而言,java的读写操作又分为两种:字符流和字节流. 实际上字节流在操作时本身不会用到缓冲区(内存),是文件本身直接操作的,而字符流在操作时使用了缓冲区,通过缓冲区再操作文件. 什么是流? ...

  9. 三、编译第一步 make xxx_defconfig——Makefile.build 脚本

    3.1 上章分析回顾 3.1 上章分析出的参数 3.1.1 变量 MAKECMDGOALS = xxx_defconfig KBUILD_EXTMOD = version_h := include/g ...

  10. HDU2444 The Accomodation of Students【匈牙利算法】

    题意: 有n个学生,有m对人是认识的,每一对认识的人能分到一间房,问能否把n个学生分成两部分,每部分内的学生互不认识,而两部分之间的学生认识.如果可以分成两部分,就算出房间最多需要多少间,否则就输出N ...