前言
本题为 LeetCode 前 100 高频题
本题由于没有合适答案为以往遗留问题,最近有时间将以往遗留问题一一完善。
我们社区陆续会将顾毅(Netflix 增长黑客,《iOS 面试之道》作者,ACE 职业健身教练。)的 Swift 算法题题解整理为文字版以方便大家学习与阅读。
LeetCode 算法到目前我们已经更新到 29 期,我们会保持更新时间和进度(周一、周三、周五早上 9:00 发布),每期的内容不多,我们希望大家可以在上班路上阅读,长久积累会有很大提升。
不积跬步,无以至千里;不积小流,无以成江海,Swift社区 伴你前行。如果大家有建议和意见欢迎在文末留言,我们会尽力满足大家的需求。
难度水平:困难
1. 描述
给定一个字符串 s
和一个字符串数组 words
。 words
中所有字符串 长度相同。
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 基础为核心的技术内容,也整理收集优秀的学习资料。