当我写出分割回文串的时候,面试官:那我走?

分割回文串

题目:
给出一个字符串s,分割s使得分割出的每一个子串都是回文串
计算将字符串s分割成回文分割结果的最小切割数
例如:给定字符串s="aab",
返回1,因为回文分割结果["aa","b"]是切割一次生成的。

解题思路:
还是抓住动态规划的解题步骤
初始值
递推的过程
转移方程
打印 dp 数组
返回值

最开始还是要先进行特殊值处理
当字符串的长度为0,1 的时候,是不需要进行分割的!例如:

s = "";
s = "A";

即:

if(s.length() == 0 || s.length() == 1) return 0;

递推的过程很是玄幻的感觉。而分割字符串我们首先应该做的是假设他全部都不是回文串,例如:ABCDEFG
所以我们需要给dp一个初始值

		int[] dp = new int[s.length()+1];
        for(int i = 0;i < s.length()+1;i++) {
            dp[i] = i-1;
        }

现在来递推,当然我们需要给一个易于理解的例子:ABCCBD;
同时,也需要双指针 i ,j
i 代表当前字符串
j 代表左边界
dp[i] 中存放的内容是分割的次数

i = 3 的时候表示的是当前字符串的内容是"ABC"
j 的含义是从 j 到 i 是否存在回文串,j = 1 表示的是当前字符串是"BC"

只有A的时候——》 F(1) = 0;
只有AB的时候——》 F(2) = F(1) + 1 = 1;
只有ABC的时候——》 F(3) = F(2) + 1 = 2;
只有ABCC的时候——》 F(4)Math.min (F(3) + 1,F(2) + 1) = 2;

我们可以选择在 A B C 的基础上再分割一次,或者在 A B 的基础上分割一次

只有ABCCB的时候——》 F(5) = Math.min(F(1) + 1,F(4) + 1) = 2;

在 A 的基础上分割一次,或者在 ABCC 的基础上分割一次

全部ABCCBD的时候——》F(6) = F(5) + 1 = 2;

递推完成:我们发现,当只有存在回文串的时候,才会进入我们才会面临选择:是在前一个字符的基础上分割一次,还是在回文串的开始到结束的基础上分割一次。

转移方程:

if(当前字符串是否是回文串) {
	F(i) = Math.min(dp[i],dp[j]+1};
}

返回值

dp[s.length];

我们来看一下整体代码

    public static int minCut (String s) {
    	//特殊处理
        if(s.length() == 0 || s.length() == 1) return 0;
        int[] dp = new int[s.length()+1];
        //初始化
        /*至于 `dp[0] = -1
        如果这个字符串本身就是回文串的话,
        当 i = s.length, j == 0 的时候,
        进入转移方程 dp[i] = min(dp[0] + 1,dp[i]) = 1 
        */
        for(int i = 0;i < s.length()+1;i++) {
            dp[i] = i-1;
        }
        for(int i = 1;i < s.length()+1;i++) {
            for(int j = 0;j < i;j++) {
            	//判断是否是回文
                if(isX(s,i-1,j)) {
                    dp[i] = Math.min(dp[j]+1,dp[i]);
                }
            }
            //打印dp数组内容
            System.out.println(Arrays.toString(dp));
        }
        return dp[s.length()];
    }

	//判断回文串
    public static boolean isX(String str,int i,int j) {
        while(j < i) {
            if(str.charAt(j) != str.charAt(i)) {
                return false;
            }
            i--;
            j++;
        }
        return true;
    }

来看下dp数组

在这里插入图片描述

大概过程就是这个哑子,这个解法由于有判断是否是回文序列,时间复杂度接近O(n^2) * O(n) = O(n^3)。

优化:
将回文序列的判断变为一个二维数组

    public static boolean[][] isPal(String s) {
        int len = s.length();
        boolean[][] mat = new boolean[len][len];
        for (int i = len-1; i >= 0; i--) {
            for (int j = i; j < len; j++) {
                if (i == j) {//i == j 的时候 s.charAt(i) == s.charAt(j)
                    mat[i][j] = true;
                }else if (i+1 == j && s.charAt(i) == s.charAt(j)) {//字符相邻且相等
                        mat[i][j] = true;
                }else {//不相邻但是相等且s.charAt(i)——s.charAt(j)包裹的里面一层是回文序列
                    mat[i][j] = s.charAt(i) == s.charAt(j) && mat[i+1][j-1];
                }
            }
        }

        for (int i = 0; i < len; i++) {
            for (int j = 0; j < len; j++) {
                System.out.print(mat[i][j] + " ");
            }
            System.out.println();
        }
        return mat;
    }

在这里插入图片描述
优化后的代码:

    public static int minCut1(String s) {
        if (s.length() == 0 || s.length() == 1) return 0;
        boolean[][] mat = isPal(s);
        int[] dp = new int[s.length()+1];
        for(int i = 0;i < s.length()+1;i++) {
            dp[i] = i-1;
        }
        for(int i = 1;i < s.length()+1;i++) {
            for(int j = 0;j < i;j++) {
                if(mat[j][i-1]) {
                    dp[i] = Math.min(dp[j]+1,dp[i]);
                }
            }
            System.out.println(Arrays.toString(dp));
        }
        return dp[s.length()];
    }

冲啊!!!
如有错误,多多指教!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值