LeetCode - #30 串联所有单词的子串(Top 100)

简介: 不积跬步,无以至千里;不积小流,无以成江海,Swift社区 伴你前行。如果大家有建议和意见欢迎在文末留言,我们会尽力满足大家的需求。

前言

本题为 LeetCode 前 100 高频题

本题由于没有合适答案为以往遗留问题,最近有时间将以往遗留问题一一完善。

我们社区陆续会将顾毅(Netflix 增长黑客,《iOS 面试之道》作者,ACE 职业健身教练。)的 Swift 算法题题解整理为文字版以方便大家学习与阅读。

LeetCode 算法到目前我们已经更新到 29 期,我们会保持更新时间和进度(周一、周三、周五早上 9:00 发布),每期的内容不多,我们希望大家可以在上班路上阅读,长久积累会有很大提升。

不积跬步,无以至千里;不积小流,无以成江海,Swift社区 伴你前行。如果大家有建议和意见欢迎在文末留言,我们会尽力满足大家的需求。

难度水平:困难

1. 描述

给定一个字符串 s 和一个字符串数组 wordswords 中所有字符串 长度相同

s 中的 串联子串 是指一个包含 words 中所有字符串以任意顺序排列连接起来的子串。

例如,如果 words = ["ab","cd","ef"], 那么 "abcdef""abefcd""cdabef""cdefab""efabcd", 和 "efcdab" 都是串联子串。 "acdbef" 不是串联子串,因为他不是任何 words 排列的连接。
返回所有串联字串在 s 中的开始索引。你可以以 任意顺序 返回答案。

2. 示例

示例 1

输入:s = "barfoothefoobarman", words = ["foo","bar"]
输出:[0,9]
解释:因为 words.length == 2 同时 words[i].length == 3,连接的子字符串的长度必须为 6。
子串 "barfoo" 开始位置是 0。它是 words 中以 ["bar","foo"] 顺序排列的连接。
子串 "foobar" 开始位置是 9。它是 words 中以 ["foo","bar"] 顺序排列的连接。
输出顺序无关紧要。返回 [9,0] 也是可以的。

示例 2

输入:s = "wordgoodgoodgoodbestword", words = ["word","good","best","word"]
输出:[]
解释:因为 words.length == 4 并且 words[i].length == 4,所以串联子串的长度必须为 16。
s 中没有子串长度为 16 并且等于 words 的任何顺序排列的连接。
所以我们返回一个空数组。

示例 3

输入:s = "barfoofoobarthefoobarman", words = ["bar","foo","the"]
输出:[6,9,12]
解释:因为 words.length == 3 并且 words[i].length == 3,所以串联子串的长度必须为 9。
子串 "foobarthe" 开始位置是 6。它是 words 中以 ["foo","bar","the"] 顺序排列的连接。
子串 "barthefoo" 开始位置是 9。它是 words 中以 ["bar","the","foo"] 顺序排列的连接。
子串 "thefoobar" 开始位置是 12。它是 words 中以 ["the","foo","bar"] 顺序排列的连接。

提示:

  • 1 <= s.length <= 10^4
  • 1 <= words.length <= 5000
  • 1 <= words[i].length <= 30
  • words[i]s 由小写英文字母组成

3. 答案

func findSubstring(_ s: String, _ words: [String]) -> [Int] {
   

  if words.count == 0 {
   
      return []
  }
  let perWord = words.first!.count

  if s.count < perWord * words.count {
   
      return []
  }
  //找出所有words里面的单词作为key,个数作为value,存到字典里面
  var wordsDic = [String: Int]()
  for word in words {
   

      let num = wordsDic[word]
      wordsDic[word] = (num ?? 0) + 1
  }

  let chars = Array(s)
  //存放结果的数组
  var result = [Int]()
  //遍历s字符串,创建新字典currentWordsDic存单词:个数。比较currentWordsDic和wordsDic个数
  var i = 0
  while i <= chars.count - perWord * words.count && i < perWord {
   

      var currentWordsDic = [String: Array<Int>]()//key:单词,value:数组index

      var startIndex = i
      var curIndex = startIndex

      while startIndex <= s.count - perWord * words.count {
   

          var curStr = ""
          for k in curIndex..<curIndex+perWord {
   
              curStr += String(chars[k])
          }

          if (wordsDic[curStr] ?? 0) == 0 {
   
              //如果这个值不在原数组中
              curIndex += perWord
              startIndex = curIndex
              currentWordsDic = [String: Array<Int>]()//key:单词,value:数组index
          }else {
   

              if (currentWordsDic[curStr] ?? []).count == 0 {
   
                  currentWordsDic[curStr] = [curIndex]
              }else {
   
                  currentWordsDic[curStr]!.append(curIndex)
              }

              if wordsDic[curStr]! - (currentWordsDic[curStr] ?? []).count < 0 {
   
                  //如果这个值数量已经超出原数组,找出这个值的第一个index,并删除字典里面此index之前的数据,startIndex = 此index+3
                  let array = currentWordsDic[curStr]!
                  let index = array.first!
                  if index - startIndex < curIndex - (index + perWord) {
   

                      for j in startIndex...index {
   
                          var string = ""
                          for k in j..<j+perWord {
   
                              string += String(chars[k])
                          }
                          currentWordsDic[string]!.removeFirst()
                      }
                  }else {
   

                      currentWordsDic = [String: Array<Int>]()//key:单词,value:数组index
                      for j in (index + perWord)...curIndex {
   
                          var string = ""
                          for k in j..<j+perWord {
   
                              string += String(chars[k])
                          }
                          if (currentWordsDic[string] ?? []).count == 0 {
   
                              currentWordsDic[string] = [j]
                          }else {
   
                              currentWordsDic[string]!.append(j)
                          }

                      }
                  }
                  startIndex = index + perWord
                  curIndex += perWord
              }else {
   
                  if curIndex + perWord - startIndex == perWord * words.count {
   
                      //如果遍历完数组,即找到了与数组单词适合的p子串
                      result.append(startIndex)

                      var stringStart = ""

                      for k in startIndex..<startIndex+perWord {
   
                          stringStart += String(chars[k])
                      }
                      currentWordsDic[stringStart]?.removeFirst()
                      curIndex += perWord
                      startIndex += perWord
                  }else {
   

                      curIndex += perWord
                  }
              }

          }

      }

      i += 1
  }

  return result
}

点击前往 LeetCode 练习

关于我们

我们是由 Swift 爱好者共同维护,我们会分享以 Swift 实战、SwiftUI、Swift 基础为核心的技术内容,也整理收集优秀的学习资料。

相关文章
|
2月前
Leetcode(最后一个单词长度)
这篇文章介绍了两种解决LeetCode第58题的方法,即计算给定字符串中最后一个单词的长度,方法包括翻转字符串和逆向遍历统计。
22 0
|
2月前
【LeetCode 20】151.反转字符串里的单词
【LeetCode 20】151.反转字符串里的单词
24 0
|
4月前
|
算法
LeetCode第58题最后一个单词的长度
LeetCode第58题"最后一个单词的长度"的解题方法,通过从字符串末尾向前遍历并计数非空格字符,直接得出最后一个单词的长度。
LeetCode第58题最后一个单词的长度
|
4月前
|
算法 JavaScript Python
【Leetcode刷题Python】79. 单词搜索和剑指 Offer 12. 矩阵中的路径
Leetcode第79题"单词搜索"的Python解决方案,使用回溯算法在给定的二维字符网格中搜索单词,判断单词是否存在于网格中。
55 4
|
4月前
|
Python
【Leetcode刷题Python】生词本单词整理
文章提供了一个Python程序,用于帮助用户整理和排版生词本上的单词,包括去除重复单词、按字典序排序,并按照特定的格式要求进行打印排版。
48 3
|
4月前
|
Python
【Leetcode刷题Python】318. 最大单词长度乘积
本文提供了LeetCode题目318的Python编程解决方案,题目要求在一个字符串数组中找出两个不含有公共字母的单词,且这两个单词的长度乘积最大,如果不存在这样的两个单词,则返回0。
22 0
|
6月前
|
存储 算法 测试技术
力扣经典150题第三十三题:最小覆盖子串
力扣经典150题第三十三题:最小覆盖子串
61 1
|
6月前
|
算法 测试技术 索引
力扣经典150题第三十二题:串联所有单词的子串
力扣经典150题第三十二题:串联所有单词的子串
31 0
|
3月前
|
Unix Shell Linux
LeetCode刷题 Shell编程四则 | 194. 转置文件 192. 统计词频 193. 有效电话号码 195. 第十行
本文提供了几个Linux shell脚本编程问题的解决方案,包括转置文件内容、统计词频、验证有效电话号码和提取文件的第十行,每个问题都给出了至少一种实现方法。
LeetCode刷题 Shell编程四则 | 194. 转置文件 192. 统计词频 193. 有效电话号码 195. 第十行
|
4月前
|
搜索推荐 索引 Python
【Leetcode刷题Python】牛客. 数组中未出现的最小正整数
本文介绍了牛客网题目"数组中未出现的最小正整数"的解法,提供了一种满足O(n)时间复杂度和O(1)空间复杂度要求的原地排序算法,并给出了Python实现代码。
128 2