LeetCode 131、93 组合问题(二)

84 阅读2分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第17天,点击查看活动详情

本文还是回溯的知识,主要包括下面几题:

  1. 给定一个字符串s,请将s分割成一些子串,使每个子串都是回文串,返回s的所有可能的分割。
  2. 复原IP地址。给定一个只包含数字的字符串s,用于表示一个IP地址,这些地址可以通过在s中插入.来形成。复原所有可能的IP地址。

解题思路

LeetCode 131 分割回文串

本题的目标是将字符串进行分割,并且使得每个字符串都是回文串,因此在本题中我们需要关注字符串如何切分以及判断切分后的字符串是不是回文串。

字符串的切分本质上还是组合问题,假设当前字符串为aab,第一个子串a,之后会从ab中选取第二个,依次类推。因此按照回溯的思路,确定起始索引,从起始索引开始往后切割字符串,判断是否为回文串,如果是则对此结果进行处理,回溯即可。

判断回文串的方法有很多,例如遍历字符串将字符串头尾比较,当遍历到长度的一半时停止遍历,代码如下:

public boolean isP(String s){
    if(s.length()==1) return true;
    for(int i=0;i<s.length()/2;i++){
        if(s.charAt(i) != s.charAt(s.length()-i-1)){
            return false;
        }
    }
    return true;
}

因为回溯每次都需要判断字符串是否为回文串,因此此方法会导致时间复杂度变大,我们可以实现计算好字符串可组成的子串的结果,直接读取结果即可,常见的方法是使用动态规划来计算,在动态规划中,我们需要使用两层循环,并且因为如果左右两个边界字符相同,则是否是回文串取决于中间的串,此时需要保证中间的串已有结果,因此两层循环的外层循环需要从尾部开始,动态规划代码如下:

public boolean[][] isPa(String s){
    boolean[][] dp = new boolean[s.length()][s.length()];
    for(int i=s.length()-1;i>=0;i--){
        for(int j=i;j<s.length();j++){
            if(s.charAt(i)!=s.charAt(j)){
                dp[i][j] = false;
            }else if(s.charAt(i)==s.charAt(j)&&j-i<3){
                dp[i][j] = true;
            }else {
                dp[i][j] = dp[i+1][j-1];
            }
        }
    }
    return dp;
}

最终本题的回溯代码为:

public List<List<String>> partition2(String s) {
    boolean[][] dp = isPa(s);
    backtrace(s, 0, new ArrayList<>(), dp);
    return resPa;
}

public void backtrace(String s, int cur, ArrayList<String> temp, boolean[][] dp){
    if(cur == s.length()){
        resPa.add(new ArrayList<>(temp));
        return;
    }
    for(int i=cur;i<s.length();i++){
        String s1 = s.substring(cur, i+1);
        if(!dp[cur][i]) continue;
        temp.add(s1);
        backtrace(s, i+1, temp, dp);
        temp.remove(temp.size()-1);
    }
}

LeetCode 93 复原IP地址

本题还是使用回溯法,根据IP地址的定义我们可以知道,IP地址里面有三个点将四个数字分隔开,因此我们可以使用一个集合来接收四个数字,回溯的终止条件即为集合的元素为4且此时已经遍历到了最后一个字符,即回溯需要的参数也已确定:字符串、当前遍历的字符索引以及存储中间结果的集合。IP地址的每个数字必须小于等于255且数字不能以0开头,得到这些条件,我们可得代码:

private List<String> resRe = new ArrayList<>();

public List<String> restoreIpAddresses(String s) {
    backtrace2(s, 0, new ArrayList<>());
    return resRe;
}

public void backtrace2(String s, int cur, ArrayList<String> temp){
    if(temp.size() == 4&&cur==s.length()){
        StringBuilder sb = new StringBuilder();
        for(int i=0;i<temp.size();i++){
            sb.append(temp.get(i)).append(".");
        }
        sb.deleteCharAt(sb.length()-1);
        resRe.add(sb.toString());
        return;
    }
    for(int i=cur;i<s.length();i++){
        String s1 = s.substring(cur, i+1);
        if(s1.length()>3||Integer.parseInt(s1)>255||(s1.length()>1&&s1.charAt(0)=='0')) continue;
        temp.add(s1);
        backtrace2(s, i+1, temp);
        temp.remove(temp.size()-1);
    }
}