1、問題描述
這個問題來自leetcode中的Longest Substring Without Repeating Characters,誠如標題所述,我們需要尋找的是在一個字符串中,沒有重復字符的最長字串。我們假定字符串中的字符只由$a$~$z$這26個字符構成。例如,對於字符串"$abcabcbb$",它的無重復字符最長字串是"$abc$",長度為3;對於字符串"$bbbb$",它的無重復最長字串是$b$,長度為1。
2、算法一
我們能夠立即想到的,最原始的算法就是,從字符串的每一個位置開始構造字串,並逐漸增大字串長度,直到碰到一個已經出現在這個字串中的字符為止,這樣對於長度為$n$的字符串而言,經過$n$次遍歷即可求得最長的無重復字符字串。實際上,考慮到構成字符串的字母表大小只有26,每次遍歷最多也只需進行26次增長子串的操作,這個算法的整個時間復雜度為$O(26n)$。這個算法很簡單也很好理解,不再給出詳細代碼。
3、算法二
算法二能夠將時間復雜度縮小到$O(n)$,即只遍歷一遍字符串即可。它主要基於一個思想:當我們在構建子串的時候,如果遇到了一個重復字符,那么我們可以使遍歷跳躍一定“距離”,直接忽略掉不必要的搜索。對於一個字符串$A=a_{0}a_{1}a_{2}a_{3}...a_{n-1}$,我們正在構造的字串是$B=a_ja_{j+1}...a_k$,接下來一個字符是$a_{k+1}$,但是它與當前$B$中的某一字符$a_{k'}$相同。那么對於那些所有以$a_i$,$(j+1 \le i < k)$開頭的無重復字符最長字串的長度一定小於$B$的長度。例如,對於字符串$A=$"$abcdcef$",$B=$"$abcd$",當前長度為4,下一個字符是$c$,而它與$b_3$相同,那么在$A$中,以$a_1,a_2,a_3$開頭的無重復字符最長字串,相對於$B$,“更早地”遇到了重復字符$c$,它們的長度都要小於$B$的長度。
為了實現算法二,我們需要額外使用一個大小為26的數組$pos$以及一個變量$basePos$,$pos$用來記錄當前情況下,每個字符最近出現的那個位置,而$basePos$用來記錄所增長的子串的開始位置。初始情況下,$basePos=0$,表示第一個所增長的那個子串開始於位置0,$pos$中的每個元素都初始為-1。對於當前的字串,我們如何判斷下一個字符是否已經出現在了當前字串中呢?例如,對於字符$a$,我們檢查$pos[0] \le basePos$是否成立,成立的話,則說明$a$已經出現在當前字串中,弄明白需要搞清楚$pos$與$basePos$的含義。
我們使用一個例子來說明這個過程。
初始情況

Step:1

Step:2

Step:3

Step:4

Step:5

注意,這里是由於pos['c'-'a']=2>basePos,也就是第二個c與前面的c重復了。
Step:6

Step:7

下面我們給出代碼,我們需要處理某些特殊情況,例如輸入字符串為空串等等。
int lengthOfLongestSubstring(string s) { int pos[26]; int i; for(i=0;i<26;i++) { pos[i]=-1; } int basePos=0; int maxLength=-1; for(i=0;i<s.length();i++) { if(pos[s[i]-'a']<basePos) { pos[s[i]-'a']=i; } else { if(maxLength==-1||i-basePos>maxLength) maxLength=i-basePos; basePos=pos[s[i]-'a']+1; pos[s[i]-'a']=i; } } if(s.length()-basePos>maxLength||maxLength==-1) maxLength=s.length()-basePos; return maxLength; }
