LeetCode刷题——190325

本文深入解析LeetCode上的三道经典题目:单词拆分II、分割回文串及分割回文串II,提供了多种解题思路,包括递归、动态规划等方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Leetcode140.单词拆分Word-break-ii

题目:

给定一个非空字符串 s 和一个包含非空单词列表的字典 wordDict,在字符串中增加空格来构建一个句子,使得句子中所有的单词都在词典中。返回所有这些可能的句子
在这里插入图片描述

思路:

1.尾部追加法
我想到了要用递归去做,但是我的想法是从头递归!此处从尾递归效率更高。除此之外,我的想法不是很能处理ArrayList的追加操作。我一开始的想法是,构造一个stringbuffer的变量,不断地追加被分割出来的单词,然后递归。
在这里插入图片描述
自己的思路错误在于:

  1. 没有想到最后一个字符的处理方式,可以通sb.deleteCharAt(sb.length()-1)解决最后一个空格问题。
  2. 如果用sb进行追加(代码中使用temp的地方)操作,不能解决回调问题(因为返回上层递归需要删除本次递归中追加的字符,具体的个数不好提取,但是也是可以知道的,只不过这张做法比较麻烦)。也就是说,加入遇到 catsanddogs
    那么cat sand dog和 cats and dog两者都可能.我一开始的想法是:递归判断完dict是否包含剩余字符串中的字符之后,令sb=null,然后重新添加sb.subString,问题在于,sb=null之后,前面保存的内容也将不存在,所以最后res追加只可能为空。

代码1:

    ArrayList<String> res = new ArrayList<>();   
    ArrayList<String> temp = new ArrayList<>();
    public ArrayList<String> wordBreak(String s, Set<String> dict) {    
        dfs(s,dict);    
        return res;    
    }   
        
    private void dfs(String s ,Set<String> dict) {    
     if(s.length()<1) {    
        StringBuffer sb = new StringBuffer();    
        for(String str : temp)    
            sb.append(str).append(" ");        //按照substring分成多个单词    
        sb.deleteCharAt(sb.length()-1);    
        res.add(sb.toString());    
     }

     for(int i=s.length()-1;i>=0;--i) {

        String str = s.substring(i,s.length());//从i开始一直到最后一个字符都包含在内,则加入temp    
        if(dict.contains(str)) {    
            temp.add(0, str);  //从头开始添加,所以需要从尾遍历(如果从头遍历的话,每次递归remove的应该是temp中最后一个元素)
            dfs(s.substring(0, i),dict);  
            //回到dfs(s.substring(0, i),dict)操作之前的状态,因为每次只递归追加一个str,删除这个插入的str回到上层递归             
            temp.remove(0);   
        }    
     }    
    }

思路2:

递归时间太长,采用map存储可能的情况(好好研究,这种方法,可以适合各种顺序的输出)
该思路是从头开始遍历,并且从dict的角度出发进行思考。
使用一个map存放:key对应以某个单词开头直至结尾的str,value为其对应分割后的结果
比如:

keyvalue
dogdog
sanddogsand dog
catsanddogcat sand dog

结束以cat开头的一次遍历(for(substr:dict))将整个s:catsanddog放入map中并return最终的res
开始以cats的第二次遍历

keyvalue
dogdog
sanddogsand dog
catsanddogcat sand dog,cats and dog(应该继续追加的,但是怎么追加还没搞清楚)
anddogand dog

(此时map中已经保留了dog那么,dog对应的res可以直接get,只需要追加两个即可)

代码2:

    public ArrayList<String> wordBreak(String s, Set<String> dict) {    
        return DFS(s,dict,new HashMap<String,ArrayList<String>>());    
    }   

    private ArrayList<String> DFS(String s, Set<String> dict,HashMap<String,ArrayList<String>>  map){    
     if(map.containsKey(s))    
        return map.get(s);    
     
     ArrayList<String> res = new ArrayList<>();
         if(s.length()<1) {    
        res.add("");    
        return res;    
     }    
     for(String subStr : dict) {    
        if(s.startsWith(subStr )) {//但凡字符串s中有以字典中subStr开头的字符串,那么将subStr加入res    
            for(String str : DFS(s.substring(subStr.length()), dict, map)) //从subStr这个词结束向后递归    
                res.add(subStr+(str==""?"":" ")+str);//str==""?"":"    "的意思是,如果有字符串就加空格,没有字符串了就不加。
            }
         }
         map.put(s, res);
         return res;
        }



LeetCode131分割回文串palindrome-partitioning

题目:

给定一个字符串 s,将 s 分割成一些子串,使每个子串都是回文串。返回 s 所有可能的分割方案。
在这里插入图片描述

思路:

递归调用。重点在于,helper中需要传递一个new ArrayList temp,用以保留上一轮结果。
Temp的初始化,用上一轮结果list初始化!否则,每一轮的temp都是只包含本轮结果的值!(我自己写的错在这)
注意这种返回值是List<List>类型的问题
DFS当打中的 ArrayList temp = new ArrayList<>(list);
注意:

  • 新建一个ArrayList。为什么不直接用list的原因在于结果错误!!原因未知,研究研究

    • 如果只用唯一的list传递,那么所有的追加操作都将在list之后进行
    • 那么每次调用完dfs下一次递归后,应该list.remove新插入的字符串,返回上一级(删除操作错误,返回空)
      在这里插入图片描述
  • 将上次递归产生的结果list作为参数传入新建的temp中,以保证追加操作在上一步完成后继续

代码:

ArrayList<ArrayList<String>> res = new ArrayList<ArrayList<String>> ();    
public ArrayList<ArrayList<String>> partition(String s) {    
     dfs(s, new ArrayList<String>());    
     return res;    
 }    
       
private void dfs(String s,ArrayList<String> list) {
     if(s.length()<1) { //如果已经没有字符串可以分割了,那么就将list加入res中,然后返回res;    
        res.add(list);    
        return;    
     }    
     for(int i = 0;i<s.length();++i) {    
        ArrayList<String> temp = new ArrayList<>(list); //作为一条记录返回。    
        if(check(s.substring(0,i+1))) {    
            temp.add(s.substring(0,i+1));
            //在i相同的情况下该条记录的temp继续传入后期的记录        
            dfs(s.substring(i+1),temp);
        }    
     }    
    }    
        
    private boolean check(String s) {    
     int i=0,j=s.length()-1;    
     while(j>i) {    
        if(s.charAt(i)==s.charAt(j)) {    
            ++i;    
            --j;    
        }else    
            return false;    
     }    
     return true;    
    }



leetCode 132. 分割回文串②

题目:

给定一个字符串 s,将 s 分割成一些子串,使每个子串都是回文串。
返回符合要求的最少分割次数。

思路1:动态规划

在这里插入图片描述
在这里插入图片描述

代码1:

 public int minCut(String s) {
     int[][] dp = new int[s.length()][s.length()];
     int[] cut = new int[s.length()+1];    

     for(int i = s.length()-1;i>=0;--i) {
        cut[i]=Integer.MAX_VALUE;
        for(int j =i;j<s.length();j++) {
            if(s.charAt(i)==s.charAt(j)&&(j-i<=1 || dp[i+1][j-1]==1)) {
                dp[i][j]=1;
                cut[i]=Math.min(1+cut[j+1], cut[i]);
            }
        }
     }
     return cut[0]-1;
}

思路2:暴力求解

构建一个一维数组DP[i],里面存放的是:从0,位置i上的字符,需要分割的次数
关键点在于:dp[i-1]+1=dp[i]

代码2:

private bool isPalindrome(String s){//判断是否回文
    int first=0;
    int end=s.size()-1;
    while(first<end) {//此处不能等于,否则死循环
        if(s[first++]!=s[end--])
            return false;
    }
    return true;
}

 public int minCut(String s) {
    if(isPalindrome(s)) return 0;//首先进行整体判断,如果整体是个回文串则不需分割
    int n=s.length();
    int[] dp=new int[n];
    dp[0]=0;
    //考虑dp[i]的计算
    //1.[0~i-1]不构成回文字符,但是[0-i]构成回文字符,dp[i]=0
    //2.【0~i】不构成回文符,需要进行内部分割,遍历寻找分割点,得到分割次数最小的结果
     for(int i=1;i<n;i++)
    {
        dp[i]=dp[i-1]+1;//i位置的字符可能于前i-1个不构成回文串,所以需要在dp[i-1]的基础上+1
        if(isPalindrome(s.substr(0,i+1)))//如果[0,i]构成回文串,那么dp[i]=0,即不需要分割
            dp[i]=0;
        else
            for(int j=1;j<i;j++)
            {
                if(isPalindrome(s.substr(j,i-j+1)))//在[0,i]中判断需要分割几次
                    dp[i]=((dp[j-1]+1)<dp[i])?(dp[j-1]+1):dp[i];//如果在[0,i]中分割之后得到的结果比dp[i-1]+1小(或者是上一个j得到的结果小,那么更新dp[i])
            }
    }
    return dp[n-1];
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值