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 ☆☆☆的更多相关文章

  1. LeetCode - 30. Substring with Concatenation of All Words

    30. Substring with Concatenation of All Words Problem's Link --------------------------------------- ...

  2. [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 ...

  3. 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 ...

  4. [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 ...

  5. 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 ...

  6. [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 ...

  7. LeetCode 30 Substring with Concatenation of All Words(确定包含所有子串的起始下标)

    题目链接: https://leetcode.com/problems/substring-with-concatenation-of-all-words/?tab=Description   在字符 ...

  8. [Leetcode][Python]30: Substring with Concatenation of All Words

    # -*- coding: utf8 -*-'''__author__ = 'dabay.wang@gmail.com' 30: Substring with Concatenation of All ...

  9. 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 ...

随机推荐

  1. Python Pygame (2) 事件

    程序在运行期间会产生许许多多的事件,事件随时可能发生(如移动鼠标,点击鼠标,敲击键盘按键),Pygame的做法是将所有的事件都放到事件队列里,通过for循环语句迭代取出每一条事件,然后处理关注的事件即 ...

  2. Scrum立会报告+燃尽图(十月二十一日总第十二次)

    此作业要求参见:https://edu.cnblogs.com/campus/nenu/2018fall/homework/2246 项目地址:https://git.coding.net/zhang ...

  3. 第六周的PSP

    本周PSP: 本周进度条: 累积进度图:: 本周PSP饼状图:

  4. POJ 2484(对称博弈)

    题目链接:http://poj.org/problem?id=2484 这道题目大意是这样的,有n个硬币围成一圈,两个人轮流开始取硬币(假设他们编号从1到n),可以选择取一枚或者取相邻的两枚(相邻是指 ...

  5. 线段树---poj2528 Mayor’s posters【成段替换|离散化】

    poj2528 Mayor's posters 题意:在墙上贴海报,海报可以互相覆盖,问最后可以看见几张海报 思路:这题数据范围很大,直接搞超时+超内存,需要离散化: 离散化简单的来说就是只取我们需要 ...

  6. Spring1()

    目的: 降低Java开发的复杂性 使用策略: 基于POJO的轻量级和最小侵入式的编程 通过依赖注入和面向接口实现松耦合 基于切面和惯例进行声明式编程 通过切面和模版减少样式代码 依赖注入:由Sprin ...

  7. inside、outside和dmz之间的访问

    现有条件:100M宽带接入,分配一个合法的IP(222.134.135.98)(只有1个静态IP是否够用?);Cisco防火墙PiX515e-r-DMZ-BUN1台(具有Inside.Outside. ...

  8. Linux安装weblogic

    一.软件安装 1. 安装前的准备工作 1.1 首先请确认您要安装的WebLogic版本所在的平台已通过了BEA的认证,完整的认证平台列表请参考 http://e-docs.bea.com/wls/ce ...

  9. C#控件之Repeater控件使用

    歡迎大家來討論,修改,一定虛心接受. 1.為什麼使用Repeater控件? 關於把從數據庫讀取的數據綁定到前台頁面,我們可以使用DataGrid.DataGridView以及Repeater來佈局,三 ...

  10. SQL Server 一些操作语句

    查询表结构---sp_help 表名 或 sp_columns  表名 删表 -------drop table 表名删表中所有的数据----------truncate table 表名根据条件删表 ...