例如:
輸入: "abcabcbb" 輸出: 3 解釋: 因為無重復字符的最長子串是"abc",所以其
長度為 3。
方法一:
/// <summary> /// 給定一個字符串,找出其中不含有重復字符的 最長子串 的長度。 /// </summary> /// <param name="str"></param> /// <returns></returns> public int LengthOfLongestSubstring(string str) { int resLength = 0; int strLength = str.Length; for (int i = 0; i < strLength; i++) { for (int j = i + 1; j < strLength; j++) { //這里能確定str的所有子串 HashSet<string> hashSet = new HashSet<string>(); bool isExists = false; for (int z = i; z < j; z++) { string strChildren = str.Substring(z, 1); if (hashSet.Contains(strChildren)) { isExists = true; break; } else { hashSet.Add(strChildren); } } if (!isExists) { //這里是不存在相同的才給resLength賦值 resLength = Math.Max(resLength, j - i); } } } return resLength; }
方法二:滑動窗口
在方法一中,我們會反復檢查一個子字符串是否含有有重復的字符,但這是沒有必要的。如果從索引 i 到 j - 1 之間的子字符串sij 已經被檢查為沒有重復字符。我們只需要檢查 s[j] 對應的字符是否已經存在於子字符串 sij 中。
要檢查一個字符是否已經在子字符串中,我們可以檢查整個子字符串,這將產生一個復雜度為 O(n2) 的算法,但我們可以做得更好。
通過使用 HashSet 作為滑動窗口,我們可以用 O(1) 的時間來完成對字符是否在當前的子字符串中的檢查。
滑動窗口是數組/字符串問題中常用的抽象概念。 窗口通常是在數組/字符串中由開始和結束索引定義的一系列元素的集合,即 [i, j)(左閉,右開)。而滑動窗口是可以將兩個邊界向某一方向“滑動”的窗口。例如,我們將 [i, j)向右滑動 11 個元素,則它將變為 [i+1, j+1)(左閉,右開)。
回到我們的問題,我們使用 HashSet 將字符存儲在當前窗口 [i, j)(最初 j = i)中。 然后我們向右側滑動索引 j,如果它不在 HashSet 中,我們會繼續滑動 j。直到 s[j] 已經存在於 HashSet 中。此時,我們找到的沒有重復字符的最長子字符串將會以索引 i開頭。如果我們對所有的 i 這樣做,就可以得到答案。
public int LengthOfLongestSubstring(string str) { int resLength = 0; int strLength = str.Length; int i = 0, j = 0; HashSet<string> hashSet = new HashSet<string>(); while (i < strLength && j < strLength) { string oneStrJ = str.Substring(j,1); if (!hashSet.Contains(oneStrJ)) { hashSet.Add(oneStrJ); j++; resLength = Math.Max(resLength,j-i); } else { string oneStrI = str.Substring(i, 1); hashSet.Remove(oneStrI); i++; } } return resLength; }