LeetCode 3:无重复字符的最长子串 思考分析
給定一個字符串,請你找出其中不含有重復字符的 最長子串 的長度。
示例 1:
輸入: “abcabcbb”
輸出: 3
解釋: 因為無重復字符的最長子串是 “abc”,所以其長度為 3。
示例 2:
輸入: “bbbbb”
輸出: 1
解釋: 因為無重復字符的最長子串是 “b”,所以其長度為 1。
示例 3:
輸入: “pwwkew”
輸出: 3
解釋: 因為無重復字符的最長子串是 “wke”,所以其長度為 3。
請注意,你的答案必須是 子串 的長度,“pwke” 是一個子序列,不是子串。
動態規劃解
dp[i]:到以第i個字符結尾的不包含重復字符的最大長度
對于第i個字符:
1、第i個字符從未出現過,dp[i]=dp[i-1]+1
2、第i個字符出現過:找到第i個字符最近一次出現的位置index(從第i個往前找),記兩個距離為d=i-index
【a】、d<= dp[i-1],即這個字符出現在以第i-1個字符結尾的不含重復字符的字符串中,則dp[i]=d,表示從第最近一次出現后重新計算長度:
例如:
i: 0 1 2 3 4 5
s: 1 2 3 4 5 4
dp: 1 2 3 4 5 2
最后的這個2 = 5 - 3(最近一次出現的i);
【b】、d>dp[i-1],即這個字符沒有出現在以第i-1字符結尾的不含重復的最大長度字符串中。
dp[i] = dp[i-1] +1;
例如:
i: 0 1 2 3 4 5 6
s: 1 2 3 4 5 4 1
dp: 1 2 3 4 5 2 3
s最后的這個1雖然在i=0的位置出現過,但是由于d=6-0=6>dp[5],所以dp[6]=dp[5]+1,也就是說我們的子串已經重新更新了。
參考https://zhuanlan.zhihu.com/p/112545613進行優化;
f[i]:以原字符串s中,以位置i的字符s[i]為右邊界時的最優左邊界
例如樣例 a b c a b c b b
對應的f數組為:
f[0] = 0,左邊界為第一個a,對應子串為a
f[1] = 0,左邊界為第一個a,對應子串為ab
f[2] = 0,左邊界為第一個a,對應子串為abc
f[3] = 1,左邊界為第一個b,對應子串為bca
f[4] = 2,左邊界為第一個c,對應子串為cab
f[5] = 3,左邊界為第二個a,對應子串為abc
f[6] = 5,左邊界為第二個c,對應子串為cb
f[7] = 7,左邊界為最后一個b,對應子串為b
f[0] = 0;
對于f[i],只需要考察f[i-1]到i-1這個區間中是否出現過s[i]
如果沒有出現過,則f[i]=f[i-1]
如果出現過,那么必然只出現了一次,因為f[i-1]到i-1這個區間中必然不存在重復字符,于是設s[i]出現的位置為pos,則f[i]=pos+1;在重復的字符后面的一個字符作為新的起始點。
可以發現,左邊界的更新是單調遞增的,因此s中每個字符最多也只會遍歷一遍,時間復雜度為O(n)。
由于f[i]的更新只與f[i-1]有關,因此不需要維護整個數組。
滑動窗口解
其實和dp第一種方法類似:
滑動窗口的核心思路如下:
比如輸入字符串為:abcdecfg,字串 abcde 滿足題目要求,當新加入 c 時,字串變成了 abcde c,字符 c 重復了,不滿足要求。這時候,只需要將第一個重復的字符 c 連同之前的字符丟掉,變成 de c,就又能滿足要求了。每新加一個字符,都更新最長字串的長度,遍歷完一遍之后,即可得到答案。
問題在于如何丟掉第一個重復字符連同之前的字符,其實我們不用真的丟掉,而是可以維護一個左邊界值 left ,有重復字符的話,就把 left 更新為重復字符的下一個位置,假裝丟掉了它和之前的字符。
參考鏈接:https://blog.csdn.net/m0_37433111/article/details/108743399
總結
以上是生活随笔為你收集整理的LeetCode 3:无重复字符的最长子串 思考分析的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 颐和园残疾证免票吗
- 下一篇: LeetCode 239:滑动窗口最大值