算法实验室-21-动态规划-从字符串中分割出单词问题[1]

题目描述:

为了完成这个任务,我们会分步骤的先给出一些前置的问题和解法,然后再通过这些解法的组合使用去完成本题(算法实验室的本质)

问题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点了,明天见呗...

 

 

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值