Problem
Given a string, find the length of the longest substring without repeating characters.
Examples:
Given "abcabcbb", the answer is "abc", which the length is 3.
Given "bbbbb", the answer is "b", with the length of 1.
Given "pwwkew", the answer is "wke", with the length of 3. Note that the answer must be a substring, "pwke" is a subsequence and not a substring.
Understanding
采用分治策略,對于給定的串,滿足題設(shè)的子串(substring)要么在前半段,要么在后半段,要么跨越前后兩段,取其中的最大值即可。
關(guān)鍵問題是,如何求取跨越其中兩段的最長子串。
求跨越兩段最長子串的策略
對于一個串s的兩個部分:left, right
為了求跨越left,right的最長滿足條件的子串, 首先left,right的相鄰邊界必須有字符并且不相同: left[-1], right[0], null 三者兩兩不相等。
其次,求解策略是,將令i指向left的尾部,j指向right的首部, 如果對于某一個狀態(tài)i,j 總是嘗試將現(xiàn)有的最長串?dāng)U展到i-1,j+1的范圍顯然i-1,j+1所指向的字符如果已經(jīng)存在于當(dāng)前的最長串中,則可以證明此時的指針已經(jīng)達(dá)到邊界。
循環(huán)不變式是:每次循環(huán)結(jié)束,i,j必然包括最長的串,且i非遞增,j非遞減。所以在i,j的變化過程中所求得串長度一定是非遞減的。
循環(huán)結(jié)束的條件是i,j停止變化,因而當(dāng)循環(huán)結(jié)束時能夠得到最長的串。
Code(Java)
import java.util.*;
public class LongestNoRepeat{
public static void main(String[] args)
{
Scanner in=new Scanner(System.in);
//String s="qwekewre";
System.out.println("input \"-\" to terminate");
String s="-";
while(in.hasNextLine() && !((s=in.nextLine()).equals("-")))
{
int[] res=findMost(s,0,s.length()-1);
System.out.println("i,j,s[i...j]="+res[0]+","+res[1]+" : "+s.substring(res[0],res[1]+1));
}
}
//返回int[]{i,j} 分別記錄開始和結(jié)束下標(biāo),對于非空串,不會返回null
public static int[] findMost(String s,int i,int j)
{
if(j-i==0)
return new int[]{i,j};
int mid=(j+i)/2;
int[] s1=findMost(s,i,mid);
int[] s2=findMost(s,mid+1,j);
int x=mid,y=mid+1;
boolean xvalid=true,yvalid=true; //分別記錄左右指針是否還在變化
int[] s0=null;
if(s.charAt(x)!=s.charAt(y))
{
Set<Character> set=new HashSet<Character>();
set.add(s.charAt(x));
set.add(s.charAt(y));
while(xvalid || yvalid)
{
//循環(huán)不變式:每次循環(huán)開始時, x,y必然指向當(dāng)前最長的串
//每次循環(huán)結(jié)束時, x非遞增變化, y非遞減變化
//且當(dāng)x,y都不變化時,循環(huán)退出
if(xvalid)
{
Character ch=null;
if(x==i)xvalid=false;
else if(set.contains(ch=s.charAt(x-1)))
xvalid=false;
else
{
x--;
set.add(ch);
}
}
if(yvalid)
{
Character ch=null;
if(y==j)yvalid=false;
else if(set.contains(ch=s.charAt(y+1)))
yvalid=false;
else
{
y++;
set.add(ch);
}
}
}
s0=new int[]{x,y};
}
int[] max=s0;
if(max==null || max[1]-max[0]<s1[1]-s1[0])
max=s1;
if(max[1]-max[0] < s2[1]-s2[0])
max=s2;
return max;
}
}