题目描述:
为了完成这个任务,我们会分步骤的先给出一些前置的问题和解法,然后再通过这些解法的组合使用去完成本题(算法实验室的本质)
问题1:如何将一个字符串分解成各种组合
比如abcdef->a,b,c,d,e,f,ab,cd,ef,abc,bcd,cde,def,abcd...
显然的,这并不是一个组合问题,而是一个穷举问题。
先来实现这个问题:
/**
*
* 将字符串拆分成不同长度的组合
* @param str
* @return
*/
public List<String> genWordsGroup(String str){
List<String> list = new ArrayList<String>();
//字符长度;表明一次拆分的最小单元的长度,一般从1开始
for(int i =1;i<=str.length();i++){
String arrs = "";
//开始对str进行拆分
for (int j =0;j<=str.length()-i;j++){
arrs += str.substring(j,j+i)+" ";
}
list.add(arrs);
}
return list;
}
这是一次性输出的结果:
我们姑且对这种组合方式定义一个名词:顺序组合
现在我们希望去对真实案例进行分析:"nowcoderisbest"
问题2:找出一种在词典中存在的组合,然后存储到List<String>返回
是吧...这时候,没辙了吧~如果有办法的话,你肯定不会翻到这篇博客
set的数据dict = ["now", "nowcoder","coderis","is","best"]
public List<String> genWordsGroup(String data){
List<String> list = new ArrayList<String>();
String str = data;
//字符长度;表明一次拆分的最小单元的长度,一般从1开始
for(int i =1;i<=str.length();i++){
//开始对str进行拆分
for (int j =0;j<=str.length()-i;j++){
String singleWord = str.substring(j,j+i);
if(dict.contains(singleWord)&&j == 0){
list.add(singleWord);
//重置str,i,j
str = str.substring(i);
i = 1;
j = 0;
break;
}
}
}
return list;
}
输出结果:
ok,这种方法也比较的简单,现在,继续升华问题
问题3:找出所有可能存在的集合存储到List<List<String>> list里边;
对,这个问题的核心难点就在于此,如果问题1的难度是1,那么问题2的难度就是3,而问题3的难度则是10
因为,我们其实很难从问题2的解决方案的修改从而去解决问题3,这已经达到双循环穷举说能达到的极限(个人觉得)
死盯着穷举去解决这个问题,一辈子可能都翻不出花来,所以,穷则思辨,我们现在已经是种穷途末路的感觉,我们需要新的办法去研究这个问题
所以,虽然这个题本质是个穷举的问题,但是穷举的手段有很多:
1.基于for的穷举
2.基于递归的穷举
3.基于栈和队列的穷举
我们将其归结为三种穷举工具,每一种工具都有独特的偏好,但到目前为止我还没办法阐述不同穷举手段的独特魅力
现在,我们尝试使用递归的方法去研究上述问题:
利用递归去求解第一个问题:
public void recursion(String data,int len){
//递归结束条件
if(len == data.length()+1){
return;
}
List<String> list = new ArrayList<String>();
for (int i =0;i<=data.length()-len;i++){
list.add(data.substring(i,i+len));
}
Debug.Log(list, Debug.DebugType.SingleRow);
recursion(data,len+1);
}
还是比较容易的
利用递归求解第二个问题(顺便完成问题3):
//请在构造里面初始化
List<List<String>> l2 ;
public void recursion2(String data,int len,List<String> list){
//递归结束条件
if(len == data.length()+1){
return;
}
for (int i =0;i<=data.length()-len;i++){
String singleWorld = data.substring(i,i+len);
if(dict.contains(singleWorld)&&i ==0){
list.add(singleWorld);
if(singleWorld.length() == data.length()){
l2.add(list);
list.clear();
}
recursion2(data.substring(len),1,list);
}
}
recursion2(data,len+1,list);
}
//输出结果:
...
这个时候我们可以回到题目,看一下节目效果:
//请在构造初始化
public List<String> l3;
String output = "";
public void recursion3(String data,int len){
//递归结束条件
if(len == data.length()+1){
return;
}
for (int i =0;i<=data.length()-len;i++){
String singleWorld = data.substring(i,i+len);
if(dict.contains(singleWorld)&&i ==0){
output+= singleWorld+" ";
recursion3(data.substring(len),1);
if(singleWorld.length() == data.length()){
l3.add(output);
output = "";
return;
}
}
}
recursion3(data,len+1);
}
当然,顺序可能有点不太一样
做个翻转:
显然,基于递归的穷举还有很多可以挖掘的地方,单就穷举功率而言,比起for要强大很多很多(在下喜欢称呼“能够完成任务的能力”称为“功率”)
放到牛客里面试验一下吧:
确实,时间复杂度很高,明天我们来研究优化问题,毕竟现在都凌晨1点了,明天见呗...