携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第8天,点击查看活动详情 。如果哪里写的不对,请大家评论批评。
希望往后的日子,可以每天坚持一个算法
之后的文章会按照XV小程序CodeTop的排行去刷,证明这些算法题都是在面试中经常见到的。 比如前面的 反转链表 热度排行第一。
无重复字符的最长子串
题目
给定一个字符串,找出不含有重复字符的最长子串的长度。
示例:
给定 “abcabcbb” ,没有重复字符的最长子串是 “abc” ,那么长度就是3。
给定 “bbbbb” ,最长的子串就是 “b” ,长度是1。
给定 “pwwkew” ,最长子串是 “wke” ,长度是3。请注意答案必须是一个子串,”pwke” 是 子序列 而不是子串。
分析
这个的考点主要是滑动窗口或者叫做动态规划,当然也可以使用双重循环来找到最大的值,显然不是要考我们双重循环。
滑动窗口
滑动窗口就是一个动态的窗口,这个窗口可以是固定长度,也可以是可变长度,下面我们使用图来解释一下滑动窗口和算法拆解
拆解一下我们需要的变量
- 需要一个字段来记录最大的长度
MaxLen - 需要一个字段来记录开始的位置
start - 需要一个哈希来记录数据是否存在,Swift我们可以用字典来做
dic
图解
- 遍历字符串的每一个数据,定义了
start从0开始(也就是a),如果在哈希中不存在,标记上当前的位置,char为key,下标为value 。dic[char] = i - 这个时候
start下标为0,MaxLen = i -start + 1 = 1 - i++之后,取值
b,发现哈希不存在,继续上一步的操作,标记上当前的位置,char为key,下标为value 。dic[char] = i - 这个时候
start依旧下标0开始,MaxLen = i -start + 1 = 1 = 2
- 再次i++,取值是
a,这时发现哈希中已经存在a,这表示有重复的字符,那么我们要跳过a,从a的下一个字符作为起始点,start = dic[char] + 1,图中字段是随意定义的,理解为主。 - 下一步还需要把当前新的下标记录下来,需要会覆盖原来的值。
- 这时候
start是从下标到了1的位置,MaxLen = i -start + 1 = 1 = 2这时候发现居然还是等于2,也就是说ab、ba都是2的长度,可以任意取值。为了保持MaxLen是最大值,我们一般用MaxLen = max(MaxLen, i - start + 1) - 下面就是重复上面的方法,不再文字介绍了,直接看图吧
代码
func lengthOfLongestSubstring(_ s:String) -> Int {
let list = Array(s)
var dic = [Character:Int]()
var MaxLen = 0
var start = 0
for (i,char) in list.enumerated() {
if let index = dic[char] {
start = max(start, index + 1)
}
MaxLen = max(MaxLen, i - start + 1)
dic[char] = i
}
return MaxLen
}
class Solution {
func lengthOfLongestSubstring(_ s: String) -> Int {
if s.count < 2 { return s.count }
let chs = Array<Character>(s)
var map = Array(repeating: -1, count: 128)
var maxLen = 0
var start = 0
for i in 0..<chs.count {
let char = chs[i]
let idx = Int(char.asciiValue ?? 0)
let preIdx = map[idx]
start = max(start, preIdx + 1) //"abba"的情况
maxLen = max(maxLen, i-start+1)
map[idx] = i
//print("\(chs[0...i])的最长无重复子串长度是\(maxLen), start=\(start)")
}
return maxLen
}
}