Leetcode 132: 分割回文串 II

问题

给定一个字符串 s,将 s 分割成一些子串,使每个子串都是回文串。
返回符合要求的最少分割次数。
示例:
输入: “aab”
输出: 1
解释: 进行一次分割就可将 s 分割成 [“aa”,“b”] 这样两个回文子串。

分析

因为有分割回文串I铺垫,所以最傻X的方法就是,回溯得到所有的的分割方案,然后找其中需要最少分割次数的一个,返回。结果当然是,不出所料的超时了。
下面写AC的几种方法

DP1:

dp[i][j],记录字符串s从i——j的子串是否是回文串;
count[i][j],记录将字符串s从i——j的子串,切割成子串都是回文串的最小切割次数;
一次遍历,求得dp数组
第二次遍历,其中
如果s(i,j)为回文串,则count[i][j] = 0;
否则,count[i][j] 等于
count(i,i) + count(i+1,j)
count(i,i+1) + count(i+2,j)

count(i, i+k) + count(i+k+1,j)

count(i, j-1) + count(j,j)
中的最小值+1
解释一下:
比如:count(3, 6),即s从3——6的子串,切割成回文子串的最小切割次数;
如果s(3,6)是回文串,假如说是“cdef”,count(3,6) = 0;
否则,从不同位置切割:

  • 在位置3后面切割,即c和def:count(3,3) + count(4,6)
  • 在位置4后面切割:count(3,4) + count(5,6)
  • 在位置5后面切割:count(3,5) + count(6,6)

最后在这个基础上+1,即切割位置的那一次切割。
时间复杂度:O(n4) 可以说是很烂了。

DP2:

来自:https://leetcode.com/problems/palindrome-partitioning-ii/discuss/42199/My-DP-Solution-(-explanation-and-code)
dp数组内容不变;
count[i],表示s字符串从i——n-1位置的子串,分割需要的最少分割次数;
如果dp[i][j] 为true,则有:
如果j=n-1,则count[i] = 0;
如果j 时间复杂度:O(n2)

DP3:

来自:https://leetcode.com/problems/palindrome-partitioning-ii/discuss/42213/Easiest-Java-DP-Solution-(97.36)
类似2,区别在于
count[i],记录字符串s从0——i的子串需要的最少分割次数。
如果dp[i][j]为真,
则从i——j位置不发生分割,count[j] = count[i - 1] + 1
时间复杂度:O(n2)

代码

DP1:

    public int minCut(String s) {
        // 大体不变,改进:划分子串时长度尽可能长(即从长到短遍历);找到一个可行解就返回;
		// 改进:二维数组记录从i——j的字符串需要的最少划分次数
		// dp + backtrailing
		int len = s.length();
		boolean[][] dp = new boolean[s.length()][s.length()];
		int[][] count = new int[len][len];
		// 获得dp数组
		for(int i = len - 1; i >= 0; i--){
			for(int j = i ; j < len; j++){
				if(j == i) dp[i][j] = true;
				else if(s.charAt(i) == s.charAt(j) &&(j - i <= 2 || dp[i + 1][j - 1])){
					dp[i][j] = true;
				}
			}
		}
		// 计算count数组
		for (int i = len - 1; i >= 0; i--){
			for (int j = i ; j < len; j++){
				if (j == i){ //如果是一个字符,切割次数 = 0;
					count[i][j] = 0; 
				} else if (dp[i][j]){
					count[i][j] = 0;
				} else {
					// 循环 比较不同的cut法 找最小值
					int min = Integer.MAX_VALUE;
					for (int k = i; k < j; k++){
						int temp = count[i][k] + count[k + 1][j];
						if(temp < min) min = temp;
					}
					count[i][j] = min + 1;
				}
			}
		}
		return count[0][len - 1];
    }

DP2:

	public int minCut(String s) {
        // 每日一问:为什么我这么傻X
		// https://leetcode.com/problems/palindrome-partitioning-ii/discuss/42199/My-DP-Solution-(-explanation-and-code)
		// 我编码呢,就是复制了一定要粘贴;
		// 下面有请这位复制型程序员入场;
		boolean[][] dp = new boolean[s.length()][s.length()];
		int[] count = new int[s.length()];
		for(int i = 0; i < s.length(); i++){
			count[i] = Integer.MAX_VALUE;
		}
		if(s == null) return 0;
		for(int i = s.length() - 1; i >= 0; i--){
			for(int j = i; j < s.length(); j++){
				// 更新dp,为count赋值
				if(i == j){
					dp[i][j] = true;
				}else if(i + 1 == j && s.charAt(i) == s.charAt(j)){
					dp[i][j] = true;
				}else if(s.charAt(i) == s.charAt(j)){
					dp[i][j] = dp[i + 1][j - 1];
				}
				// 根据dp,更新count
				if(dp[i][j]){
					if(j == s.length() - 1){
						count[i] = 0;
					}else{
						count[i] = count[i] < 1 + count[j + 1] ? count[i] : 1 + count[j + 1];
					}
				}
			}
		}
		return count[0];
    }
	

DP3:

	public int minCut(String s) {
        // https://leetcode.com/problems/palindrome-partitioning-ii/discuss/42213/Easiest-Java-DP-Solution-(97.36)
		boolean[][] dp = new boolean[s.length()][s.length()];
		int[] count = new int[s.length()];
		for(int i = 0; i < s.length(); i++){
			int min = i;
			for(int j = 0; j <= i; j++){
				if(s.charAt(i) == s.charAt(j) && (j + 1 > i - 1 || dp[j + 1][i - 1])){
					dp[j][i] = true;
					min = j == 0 ? 0 : Math.min(min, 1 + count[j - 1]);
				}
			}
			count[i] = min;
		}
		return count[s.length()- 1];
    }

你可能感兴趣的:(LeetCode)