一个经典问题,就是求字符串中不包含重复字符的最大子串。如果有多个这样的子串,则输出第一个。例:str=”abxacsvada”,最大不重复子串为:“bxacsv”。
我的思路其实也就是从头比较到尾来找,只是中间加了一些判断条件进行了优化。具体流程(先转化成char[] ch):
1、假设该最长子串的首字符为ch [i] (0<=i< ch.length),则找到ch[i]==ch[j] (i< j< ch.length).此时”j”也就确定了该子串有可能的最大长度了。
2、进一步确定该子串的长度,也就是要确定从ch[i]起,“j”长度的子串里有没有重复的字符,为了区分,这里定义int value=j。确认是否有ch[i+1]==ch[j] (i+1< j< value)。如果有,则进一步确定子串长度,再把此时的”j”赋值给value。
3、重复上诉2步骤直到“i+n”==value.此时的value也就是以ch[i]为起点的最大不重复子串的长度值。
代码如下:
import java.util.Scanner;
public class Main {
static String calDuplicateString(String str) {//分别以0-ch.length的字符为首字符寻找最大不重复子串
char[] ch=str.toCharArray();//转化成char数组
int max=0;//记录最大不重复子串的长度
int value;//记录以ch[i]为首字符的最大不重复子串的长度
int k=0;//记录最大不重复子串的起始下标
for(int i=0;i<ch.length;i++){//i表示起始字符的下标
if((i-1)<(max+k) && i>0 && ch[i-1]!=ch[max+k])//优化算法①
continue;
value=search(ch,i,max+k);//以ch[i]为首字符,寻找最大不重复子串,返回长度
if(max<value){//如果此时得到的长度比原来得到的长度长,那就赋值给max,并且记录下此时的首字符下标
max=value;
k=i;
}
if(max>=ch.length-i-1)//优化算法②
break;
}
return new String(ch,k,max);//返回该最大子串
}
//该方法为了寻找以ch[head]为起始字符的最大子串,其中head表示起始字符的下标,left表示最开始需要比较的起始位置
static int search(char[] ch,int head, int left) {
int m=ch.length;//表示最大子串的最后一个字符的下标
//上面思路对应的代码就是这里
for(int i=head;i<m-1;i++)③
for(int j=left;j<m;j++)
if(ch[i]==ch[j]){//一旦出现相同字符,则后面的字符都不可能是该最大子串的了
m=j;
break;
}
return m-head;//返回最大子串的长度
}
//main函数测试
public static void main(String[] args){
Scanner in = new Scanner(System.in);
String _str;
_str = in.nextLine();
System.out.println(calDuplicateString(_str));
}
}
原始思路就是暴力解法,中间我加了两步优化,其实严格算三步。代码中标号处为优化的地方,逐一解释:
①:假设String str=”abcdbacdjas”。当第一次寻找以第一个字符为首字符的最长子串时,找的最长子串为“abcd”,此时我们寻找以第二个字符为首字符的最大子串,分析一下:
对于第一次找到的结果来看,前四个字符是没有重复字符的,并且说明第五个字符肯定与前四个字符中的某一个相同(不考虑到末尾的情况)。当且仅当第一个字符等于第五个字符时,第二个字符为首字符的最大子串的长度才有可能大于等于第一次找到的最大子串。因为如果ch[0]!=ch[4],那么肯定存在ch[i](0< i <4)==ch[4]。所以以第二个字符为首字符的最大子串的长度不可能大于3了。所以得出三个判断条件:
(i-1)<(max+k) && i>0 && ch[i-1]!=ch[max+k]
i>0:因为第一次时没有前面得出的最大子串。
ch[i-1]!=ch[max+k]:也就是上面分析得出的判断式
(i-1)<(max+k):例如上面,所有分析都是比较前面得出的最大子串与第五个字符是否相等,那么显然不能比较大于第五个字符后的字符了。不然如果一直没有寻找到大于原来的最大子串的最大子串,那么max+k始终不变,而i-1已经>=max+k了,这样就会continue掉很多不该continue的地方。
②:max>=ch.length-i-1:这个好理解,就是寻找以ch[i]为首字符的最大子串时,如果ch[i]加上它后面的所有字符如果都不能大于之前得到最大子串的长度max时,那么,后面的这些字符都没有去寻找的必要了。
③:这里是缩小了两个循环的范围,假设:在寻找以第一个字符为首字符的最大子串时,因为不知道最开始有多长,所以需要设为ch.length,如果此时找到ch[0]==ch[5],那么此时最大子串的长度就<=5了,所以此时还需要判断ch[1]到ch[4]里有没有重复字符就可以了。而left是根据上一次得出的最大子串来确定的,上面已经①中分析过了。
上面代码有可能还存在bug,毕竟我没有把所有特殊情况都测试一遍。