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
采用分治策略,对于给定的串,满足题设的子串(substring)要么在前半段,要么在后半段,要么跨越前后两段,取其中的最大值即可。
关键问题是,如何求取跨越其中两段的最长子串。
求跨越两段最长子串的策略
对于一个串s的两个部分:left, right
为了求跨越left,right的最长满足条件的子串, 首先left,right的相邻边界必须有字符并且不相同: left[-1], right[0], null 三者两两不相等。
其次,求解策略是,将令i指向left的尾部,j指向right的首部, 如果对于某一个状态i,j 总是尝试将现有的最长串扩展到i-1,j+1的范围显然i-1,j+1所指向的字符如果已经存在于当前的最长串中,则可以证明此时的指针已经达到边界。
循环不变式是:每次循环结束,i,j必然包括最长的串,且i非递增,j非递减。所以在i,j的变化过程中所求得串长度一定是非递减的。
循环结束的条件是i,j停止变化,因而当循环结束时能够得到最长的串。
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} 分别记录开始和结束下标,对于非空串,不会返回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)
{
//循环不变式:每次循环开始时, x,y必然指向当前最长的串
//每次循环结束时, x非递增变化, y非递减变化
//且当x,y都不变化时,循环退出
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;
}
}