题目
给定一个字符串数组 arr,字符串 s 是将 arr 某一子序列字符串连接所得的字符串,如果 s 中的每一个字符都只出现过一次,那么它就是一个可行解。
请返回所有可行解 s 中最长长度。
解题
暴力回溯:
f(i,bool[],arr):遍历到arr中第i个字符串,bool数组标识已经出现过的字符。
f(i+1,bool[],arr)+arr[i].length()//如果bool和arr[i]没有冲突,则把arr[i]加入bool[],并且递归
回退bool,
f(i+1.bool[],maxLen,arr)
向上返回两者中大的。
优化:用32位的int代表26位的bool[],加上memo。注意单个字符串自己可能就不符合条件,可以用-1表示。
代码:
public static int maxLength(List<String> arr) {
//得到每个的长度和数字表示
int n=arr.size();
int[] len=new int[n];
int[] present=new int[n];
for (int i = 0; i < n; i++) {
len[i]=arr.get(i).length();
present[i]=toPresent(arr.get(i));
}
return maxLength(0,0,len,present);
}
//字符串->int表示哪些字符出现过
private static int toPresent(String s) {
int res=0;
for (char ch:s.toCharArray()){
int tmp=1<<((ch-'a'));
if((tmp&res)!=0) return -1;
res|=tmp;
}
return res;
}
public static int maxLength(int i,int chars, int[] len,int[] present) {
if(i==len.length){
return 0;
}
int res1=maxLength(i+1,chars,len,present);
boolean conflict=(present[i]==-1||(chars&present[i])!=0);
int res2=0;
if(!conflict){
res2=len[i]+maxLength(i+1,chars|present[i],len,present);
}
return Math.max(res1,res2);
}