您的位置:首页 > 其它

Leetcode_Palindrome Partitioning II

2015-08-22 04:15 411 查看

132. Palindrome Partitioning II

一、题目描述



跟之前 Palindrome Partitioning (/article/1842586.html)题目类似,只是变成了返回字符串的最小切割数。

二、解决思路

如果还按照之前 Palindrome Partitioning 的思路,按理是可以解决这个问题,但是不能在有效时间内解决。

所以我们想到在判断是否回文时,是不是可以进行剪枝;于是剪枝的思路就是和当前割数和最小割数比较,如果已经大于最小割数,说明这一个深度没有必要下去了。

但是我们会发现还是超时,于是我们想在深度搜索的递归过程中,是否是有重复计算的元素;比如 ababa划分为 aba b a 和 a b a b a 时,倒数两个字符是进行了重复计算的。所以我们引入了备忘录去记录,避免重复计算。这就有了我们下边的递归代码;

同时我们也用了动态规划去解决这个问题。具体可以参照下面代码。

三、java代码

1. 递归代码

这里要注意使用备忘录去减少对重复记录的计算;比如 ababa划分为 aba b a 和 a b a b a 时,倒数两个字符是进行了重复计算的。

public class Solution {
    int minCutNum;
    public int minCut(String s) {
        boolean[][] dp = new boolean[s.length()][s.length()];//dp[i][j]表示从i到j这一段字符串是否是回文
        int[][] beiwang = new int[s.length()+1][s.length()+1]; //beiwang表示备忘录,beiwang[i][k]表示从位置i开始,已经划分k块的是否判断过了

        minCutNum = Integer.MAX_VALUE;

        //先对s判断dp[i][j] 动态规划 上对三角 同时从左下角开始,每行从对角线开始初始化
        for(int i=s.length()-1; i>=0; i--) {
            for(int j=i; j<s.length(); j++) {
                dp[i][j] = s.charAt(i) == s.charAt(j) && (j-i < 2 || dp[i+1][j-1] );
            }
        }

        //对其深度搜索dfs
        track(0,0,s.length(),dp,beiwang);

        return minCutNum - 1;
    }

    public void track(int i, int k, int len, boolean[][] dp, int[][] beiwang) {
        if(beiwang[i][k] == 1)
            return;

        if(i == len) {
            minCutNum = Math.min(minCutNum, k);
            return;
        }

        beiwang[i][k] = 1;
        for(int j=len-1; j>=i; j--) {
            if(dp[i][j]==true && minCutNum > k)
                track(j+1, k+1, len, dp, beiwang);
        }

    }

}


2. 动规代码

同时使用备忘录的深度搜索也可以动态规划解决问题。因为递归的话很可能会栈溢出。下面给出动态规划实现和解释。

public class Solution {
    //int minCutNum;
    public int minCut(String s) {
        boolean[][] dp = new boolean[s.length()][s.length()];//dp[i][j]表示从i到j这一段字符串是否是回文

        //先对s判断dp[i][j] 动态规划 上对三角 同时从左下角开始,每行从对角线开始初始化
        for(int i=s.length()-1; i>=0; i--) {
            for(int j=i; j<s.length(); j++) {
                dp[i][j] = s.charAt(i) == s.charAt(j) && (j-i < 2 || dp[i+1][j-1] );
            }
        }

        int[] count = new int[s.length()+1]; //count[i]表示从i位置开始分成多少块

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

        return count[0]-1;
    }

}

//同时可以合并两个for循环,更简洁
public class Solution {
    //int minCutNum;
    public int minCut(String s) {
        boolean[][] dp = new boolean[s.length()][s.length()];//dp[i][j]表示从i到j这一段字符串是否是回文

        int[] count = new int[s.length()+1]; //count[i]表示从i位置开始分成多少块

        //先对s判断dp[i][j] 动态规划 上对三角 同时从左下角开始,每行从对角线开始初始化
        for(int i=s.length()-1; i>=0; i--) {
            count[i] = Integer.MAX_VALUE;
            for(int j=i; j<s.length(); j++) {
                dp[i][j] = s.charAt(i) == s.charAt(j) && (j-i < 2 || dp[i+1][j-1] );
                if(dp[i][j] == true)
                    count[i] = Math.min(count[i],count[j+1]+1);//计算从位置i开始的块数
            }
        }

        return count[0]-1;
    }

}


总结:代码的巧妙之处的提升在于:

1. 从Palindrome Partitioning (/article/1842586.html)中判断回文要求的一个函数变成动态规划利用二维数组来表示从 i 到 j 的字符串是否为回文。也是因为题目要求不一样。

2. 判断从位置 i 开始的分块数,从开始的深度搜索的递归算法转变成动态规划的算法,没有了栈溢出的可能。同时count从二维数组变成了一维数组。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: