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)。

  1. public class Solution {
  2. public List<Integer> findSubstring(String s, String[] words) {
  3. List<Integer> res = new ArrayList<>();
  4. if (s == null || words == null || words.length == 0) {
  5. return res;
  6. }
  7.  
  8. HashMap<String, Integer> toFind = new HashMap<>();
  9. for (String word : words) {
  10. Integer k = toFind.containsKey(word) ? toFind.get(word) + 1 : 1;
  11. toFind.put(word, k);
  12. }
  13.  
  14. int m = words.length, n = words[0].length();
  15. HashMap<String, Integer> found = new HashMap<>();
  16. for (int i = 0; i <= s.length() - m * n; i++) {
  17. found.clear();
  18. int j = i;
  19. for (; j < i + m * n; j += n) {
  20. String part = s.substring(j, j + n);
  21. Integer a = toFind.get(part);
  22. Integer b = found.get(part);
  23. if (a == null || (b != null && a <= b)) {
  24. break;
  25. }
  26. found.put(part, b == null ? 1 : ++b);
  27. }
  28. if (j == i + m * n) {
  29. res.add(i);
  30. }
  31. }
  32.  
  33. return res;
  34. }
  35. }

解法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)。

  1. public class Solution {
  2. public List<Integer> findSubstring(String s, String[] words) {
  3. List<Integer> res = new ArrayList<>();
  4. if (s == null || words == null || words.length == 0) {
  5. return res;
  6. }
  7.  
  8. HashMap<String, Integer> toFind = new HashMap<>();
  9. for (String word : words) {
  10. Integer k = toFind.containsKey(word) ? toFind.get(word) + 1 : 1;
  11. toFind.put(word, k);
  12. }
  13.  
  14. int m = words.length, n = words[0].length();
  15. HashMap<String, Integer> found = new HashMap<>();
  16. for (int first = 0; first < n; first++) {
  17. found.clear();
  18. int left = first; // 当前串联字符串的起始位置
  19. for (int curr = first; curr < s.length() && left <= s.length() - m * n; curr += n) {
  20. String part = s.substring(curr, curr + n);
  21. if (!toFind.containsKey(part)) { // 如果part不存在,清空found并从下一个位置开始
  22. found.clear();
  23. left = curr + n;
  24. continue;
  25. }
  26.  
  27. if (!found.containsKey(part)) {
  28. found.put(part, 1);
  29. } else if (found.get(part) < toFind.get(part)) {
  30. found.put(part, found.get(part) + 1);
  31. } else {
  32. // found和toFind中part数量相同,再添加就超出了,因此需要从left开始,
  33. // 找到第一个part相同的子串,将其与之前的子串删去,再将其下一个字符串为起点。
  34. while (!part.equals(s.substring(left, left + n))) {
  35. Integer x = found.get(s.substring(left, left + n));
  36. found.put(s.substring(left, left + n), x - 1);
  37. left += n;
  38. }
  39. // 此处需要从found中删除left,再添加curr,但两者相同,故可抵消
  40. left += n;
  41. }
  42.  
  43. // 如果当前串联子串的长度与数组长度相同,说明成功匹配了一个,
  44. // 将其记录到res后,从left的下一个子串再继续
  45. if (curr - left == (m - 1) * n) {
  46. res.add(left);
  47. Integer y = found.get(s.substring(left, left + n));
  48. found.put(s.substring(left, left + n), y - 1);
  49. left += n;
  50. }
  51. }
  52. }
  53.  
  54. return res;
  55. }
  56. }

[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. CSS3实现图片渐入效果

    很多网站都有那种图片渐入的效果,如:http://www.mi.com/minote/,这种效果用css3和一些js实现起来特别简单. 拿我之前做的页面来说一下怎么利用css3来实现图片渐入效果. 下 ...

  2. 王者荣耀交流协会--第2次Scrum会议

    Scrum master:袁玥 要求1:工作照片 要求2:时间跨度:2017年10月14号  6:02--6:43  共计41min 要求3:地点:一食堂二楼两张桌子旁(靠近卖方便面那边) 要求4:立 ...

  3. vmvare fusion 8

    http://jingyan.baidu.com/article/54b6b9c0f8830f2d583b47ce.html 补充:vmware tools  在上面,直接点击安装

  4. (工具类)MD5算法|时间格式转换|字符串转数字

    package vote.utils; import java.security.MessageDigest; import java.text.SimpleDateFormat; import ja ...

  5. oracle与DB2的一些架构

    首先,我们需要理解 Oracle 使用的架构,并理解它与 DB2 的不同之处.图 1 展示了 Oracle 的系统结构.将该图与 图 2 进行比较,后者显示了 DB2 的系统结构.在阅读本文的时候,为 ...

  6. Qt多线程-总结QThread-QThreadPool-QtConcurrent

    版权声明:若无来源注明,Techie亮博客文章均为原创. 转载请以链接形式标明本文标题和地址: 本文标题:Qt多线程-总结QThread-QThreadPool-QtConcurrent     本文 ...

  7. Viewpoint Meta标签

    移动web Viewpoint常用得设置方式: [布局viewpoint] = [设备宽度] = [度量viewpoint] <meta name="viewport" co ...

  8. 开源人脸识别face_recognition

    环境:python36 1.安装dlib.face_recognition windows版 下载dlib,cp后面是py版本 下载地址:https://pypi.org/simple/dlib/ 提 ...

  9. webgl学习笔记一-绘图单点

    写在前面   WebGl(全称:Web Graphics Library : web图形库) 是基于OpenGL ES 2.0的3D绘图协议.   WebGL完美地解决了现有的Web交互式三维动画的两 ...

  10. Git Gerrit Repo User Manual

                      Git Repo Gerrit User Manual Revision History   Revision # Description Date Author ...