本文參考自《劍指offer》一書,代碼采用Java語言。
題目
請從字符串中找出一個最長的不包含重復字符的子字符串,計算該最長子字符串的長度。假設字符串中只包含從'a'到'z'的字符。
思路
動態規划法:定義函數f(i)為:以第i個字符為結尾的不含重復字符的子字符串的最大長度。
(1)當第i個字符之前未出現過,則有:f(i)=f(i-1)+1
(2)當第i個字符之前出現過,記該字符與上次出現的位置距離為d
1)如果d<=f(i-1),則有f(i)=d;
2)如果d>f(i-1),則有f(i)=f(i-1)+1;
我們從第一個字符開始遍歷,定義兩個int變量preLength和curLength來分別代表f(i-1)和f(i),再創建一個長度為26的pos數組來存放26個字母上次出現的位置,即可根據上述說明進行求解。
注意:每次最大長度和字母出現位置要記得更新。
另一種思路:遍歷每個字符,把當前字符看成子字符串的末尾結點,同時更新開頭結點,詳細代碼見Longest Substring Without Repeating Characters
測試算例
1.功能測試(一個或者多個字符,全部字符不同/相同)
2.特殊測試(null,空字符串)
Java代碼
//題目:請從字符串中找出一個最長的不包含重復字符的子字符串,計算該最長子
//字符串的長度。假設字符串中只包含從'a'到'z'的字符。
public class LongestSubstringWithoutDup {
public static int maxLength(String str) {
if(str==null || str.length()<=0)
return 0;
int preLength=0; //即f(i-1)
int curLength=0; //即f(i)
int maxLength=0;
int[] pos= new int[26]; //用於存放字母上次出現的位置
for(int i=0;i<pos.length;i++)
pos[i]=-1;
for(int i=0;i<str.length();i++) {
int letterNumber = str.charAt(i)-'a';
if(pos[letterNumber]<0 || i-pos[letterNumber]>preLength) {
curLength=preLength+1;
}else {
curLength=i-pos[letterNumber];
}
pos[letterNumber]=i;
if(curLength>maxLength)
maxLength=curLength;
preLength=curLength;
}
return maxLength;
}
public static void main(String[] args) {
System.out.println(maxLength("arabcacfr")==4);
System.out.println(maxLength("a")==1);
System.out.println(maxLength("aaa")==1);
System.out.println(maxLength("abcdef")==6);
System.out.println(maxLength("")==0);
System.out.println(maxLength(null)==0);
}
}
收獲
1.函數f(i)為:以第i個字符為結尾的不含重復字符的子字符串的最大長度。而不是以第i個字符作為開頭。第i個字符作為結尾可以方便與下一個字符進行聯系。
2.學會用長度為26的數組來存放26個字母所在的位置下標。
