[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.
For example, given:
s: "barfoothefoobarman"
words: ["foo", "bar"]
You should return the indices: [0,9].
(order does not matter).
解法1:
题目要求在给定一个字符串s和一个字符串数组words(有m个字符串,长度均为n)的条件下,找出s中所有“由words中全部字符串按任意顺序串联而成”的子串。
需要用到两个哈希表,第一个存储words数组,key为每一个word,value为出现的次数(考虑到数组中可能会有相同的字符串);第二个哈希表用于存储当前遍历到的子串,value为已出现次数。从s的第一个字符开始遍历,按顺序找到长度为n的子串part,判断part是否在第一个哈希表中,以及当前已出现次数是否超过words数组中的数量,不满足条件的跳出循环并从s的下一个字符继续寻找。
时间复杂度为 O(n2)。
public class Solution {
public List<Integer> findSubstring(String s, String[] words) {
List<Integer> res = new ArrayList<>();
if (s == null || words == null || words.length == 0) {
return res;
}
HashMap<String, Integer> toFind = new HashMap<>();
for (String word : words) {
Integer k = toFind.containsKey(word) ? toFind.get(word) + 1 : 1;
toFind.put(word, k);
}
int m = words.length, n = words[0].length();
HashMap<String, Integer> found = new HashMap<>();
for (int i = 0; i <= s.length() - m * n; i++) {
found.clear();
int j = i;
for (; j < i + m * n; j += n) {
String part = s.substring(j, j + n);
Integer a = toFind.get(part);
Integer b = found.get(part);
if (a == null || (b != null && a <= b)) {
break;
}
found.put(part, b == null ? 1 : ++b);
}
if (j == i + m * n) {
res.add(i);
}
}
return res;
}
}
解法2:
仔细研究上面的解法,会发现其中有很多多余的步骤。例如,s="abcdefghijklmn",n=3:第一次遍历从'a'开始,需要遍历"abc", "edf", "ghi"....;而第4次比较从'd'开始,又需要遍历"def", "ghi"....
可以改变上面的思路,从一个字符一个字符遍历,变成一个单词一个单词遍历。将s按照word的长度n进行划分,如s长度为18,m=2,n=3,那么遍历的顺序为:第一次(0,3,6,9,12,15),第二次(1,4,7,10,13,16),第三次(2,5,8,11,14,17)。
首先还是先将words中的单词存到哈希表toFind中。然后从0开始遍历,用left记录当前串联起来的子串的起始位置,curr表示当前的子串(长为n),哈希表found纪录目前串联子串中的每一个word情况。遍历时分以下几种情况:
- curr在toFind中不存在,此时匹配中断,前面从left开始匹配到的都失效。因此,清空found,从下一个子串(curr+n)继续。
- curr在toFind中存在,但在found中不存在,说明前面没有匹配到。因此,将curr记入found中,然后从下一个子串继续。
- curr在toFind和found中都存在,并且found中的数量小于toFind中的数量。将found中的currz数量加一,然后从下一个子串继续。
- curr在toFind和found中都存在,并且两者数量相同,此时再将curr记入found就超过数量限制了。因此,需要从left开始,找到最早出现的同curr相同的子串(设为dupl),并把dupl同之前的子串在found中的纪录删除,然后从left移到dupl的下一个子串,然后curr从curr的下一个子串继续。
每次curr操作结束后,比较当前串联子串的长度是否与与words中所有word长度之和相等,相等则说明当前串联子串匹配成功,将left记入res中,然后从left的下一个子串继续进行。。。。
当 i = 0 遍历完成时,清空found,然后从 i = 1 进行下一次循环。
这个算法实现难度比较大,但是整体的时间复杂度为 O(n)。
public class Solution {
public List<Integer> findSubstring(String s, String[] words) {
List<Integer> res = new ArrayList<>();
if (s == null || words == null || words.length == 0) {
return res;
}
HashMap<String, Integer> toFind = new HashMap<>();
for (String word : words) {
Integer k = toFind.containsKey(word) ? toFind.get(word) + 1 : 1;
toFind.put(word, k);
}
int m = words.length, n = words[0].length();
HashMap<String, Integer> found = new HashMap<>();
for (int first = 0; first < n; first++) {
found.clear();
int left = first; // 当前串联字符串的起始位置
for (int curr = first; curr < s.length() && left <= s.length() - m * n; curr += n) {
String part = s.substring(curr, curr + n);
if (!toFind.containsKey(part)) { // 如果part不存在,清空found并从下一个位置开始
found.clear();
left = curr + n;
continue;
}
if (!found.containsKey(part)) {
found.put(part, 1);
} else if (found.get(part) < toFind.get(part)) {
found.put(part, found.get(part) + 1);
} else {
// found和toFind中part数量相同,再添加就超出了,因此需要从left开始,
// 找到第一个part相同的子串,将其与之前的子串删去,再将其下一个字符串为起点。
while (!part.equals(s.substring(left, left + n))) {
Integer x = found.get(s.substring(left, left + n));
found.put(s.substring(left, left + n), x - 1);
left += n;
}
// 此处需要从found中删除left,再添加curr,但两者相同,故可抵消
left += n;
}
// 如果当前串联子串的长度与数组长度相同,说明成功匹配了一个,
// 将其记录到res后,从left的下一个子串再继续
if (curr - left == (m - 1) * n) {
res.add(left);
Integer y = found.get(s.substring(left, left + n));
found.put(s.substring(left, left + n), y - 1);
left += n;
}
}
}
return res;
}
}
[LeetCode] 30. Substring with Concatenation of All Words ☆☆☆的更多相关文章
- LeetCode - 30. Substring with Concatenation of All Words
30. Substring with Concatenation of All Words Problem's Link --------------------------------------- ...
- [LeetCode] 30. Substring with Concatenation of All Words 解题思路 - Java
You are given a string, s, and a list of words, words, that are all of the same length. Find all sta ...
- leetCode 30.Substring with Concatenation of All Words (words中全部子串相连) 解题思路和方法
Substring with Concatenation of All Words You are given a string, s, and a list of words, words, tha ...
- [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 sta ...
- Java [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 a ...
- [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 sta ...
- LeetCode 30 Substring with Concatenation of All Words(确定包含所有子串的起始下标)
题目链接: https://leetcode.com/problems/substring-with-concatenation-of-all-words/?tab=Description 在字符 ...
- [Leetcode][Python]30: Substring with Concatenation of All Words
# -*- coding: utf8 -*-'''__author__ = 'dabay.wang@gmail.com' 30: Substring with Concatenation of All ...
- LeetCode HashTable 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 sta ...
随机推荐
- LeetCode 36. Valid Sudoku (C++)
题目: Determine if a 9x9 Sudoku board is valid. Only the filled cells need to be validated according t ...
- 未能加载文件或程序集“log4net, Version=1.2.10.0, Culture=neutral, PublicKeyToken=1b44e1d426115821”或它的某一个依赖项。系统找不到指定的文件。
在网上找了很久,很多个地方让修改配置文件,也有重装log4net的. 如文章:使用Common.Logging与log4net的组件版本兼容问题 我检查下发现项目中的package包中的Log4net ...
- mybatis update数据时无异常但没更新成功;update异常时如数据超出大小限制,造成死锁
没更新的问题原因: sqlSession.commit(); 没执行commit,但官方文档里有这样的描述:“默认情况下 MyBatis 不会自动提交事务,除非它侦测到有插入.更新或删除操作改变了数据 ...
- pixi.js 微信小游戏 入手
pixi是什么?一款h5游戏引擎 优点:简单简洁性能第一 缺点:大多数用的国产三大引擎,pixi资料少,工具少, 为什么学,装逼 用pixi开发小游戏行吗? 行.但要简单处理下 下载官网上的 weap ...
- Redis 备份数据的两种方式
既然是数据库,那就一定有数据备份方式了,而且 Redis 是内存形式的数据库,更需要数据备份了,要不然断电数据就全都丢失了. Redis 数据备份有两种方式: RDB(数据快照) AOF(记录操作日志 ...
- 方法调用时候 传入this 谁调用 传入谁
方法调用时候 传入this 谁调用 传入谁
- BZOJ4896 THUSC2016补退选(trie)
字符串扔进trie,vector记录每个前缀出现次数的最大值的更新记录即可. #include<iostream> #include<cstdio> #include<c ...
- Python常忘的进阶知识(下)
0.目录 1.装饰器 1.1 为每个函数都增加一个功能 1.2 装饰器只是一种模式 1.3 语法糖 1.4 函数需要传递参数,该如何更改装饰器? 1.5 函数需要传递关键字参数,该如何更改装饰器? 2 ...
- [cogs1065]绿豆蛙的归宿
1065. [Nescafe19] 绿豆蛙的归宿 [题目描述] 给出一个有向无环的连通图,起点为1终点为N,每条边都有一个长度.绿豆蛙从起点出发,走向终点.到达每一个顶点时,如果有K条离开该点的道路, ...
- castle activerecord 学习过程出现的问题
优点: 1.CRUD:代码简洁 2.不用配置map 3.自带事务方便 4.自带IOC 5.自带 数据有效性验证 缺点: 1.自增长(Oracle 一直提示序号不存在,有空继续尝试) 2.多条件,直接用 ...