力扣第1278题:分割回文串③(Java)——阿里2021.4.12笔试第二题

一、前言

        前面说了,从今天开始写一些比较难的题目,那么今天就来挑战回文串啦。回文串的题目还是难度标识都是困难,今天这道题目确实也没有百分百独立做出,但是经过我写完后的思考,我还是想把我的看法罗列一下。

二、题目内容

        三、题目分析

        读一下题目,发现它让我们解决的是最少分割产生回文子串的问题,因为是用动态规划解决,所以第一步还是为了确定状态转移方程的形式。经过这几天的刷题,很容易就知道dp[i][j]描述的是前i个数分割成j个数所产生的最小划分。那么dp就应该这样定义:

int len=s.length();
int [][]dp=new int [len+1][k+1];

        跟原来一样,为了防止下标问题,我们依然从1开始,到length结束。将初始值设置 

成比较大的值

       for (int i = 0; i < dp.length; i++) {
            Arrays.fill(dp[i], len);
        }

        之后两个循环,第一个i的范围从1到s.length(),进入循环后需要判断i和k的大小,然后取较小的那个作为第二个循环j的范围,因为如果k大于i,那么就是要将i个数分成k个,明显违背原则。

之后进入第二层循环后需要判断一下,当j=1的初始情况,如果j等于1,就是将前i个数分成j个,也就是不需要分割,那么就需要修改了,我们需要从两边开始逐一相互对照,最后返回一个值赋值给dp[i][j],那么对于求赋值次数的值,我们可以另外写一个函数,很简单,直接上代码:

 public int change(String s,int left,int right){//字符串s,左下标left,右下标right
        int cnt=0;
        while(left<right)
        {
            if(s.charAt(left++)!=s.charAt(right--))
                cnt++;

        }
        return cnt;
    }

那么刚刚那个第二层循环j=1的初始情况就可以写成,dp[i][j]=change(s,0,i-1);

        当j不等于1的时候呢?当我们要求dp[i][j]的时候,只需要找出前m个字符 分割成j -1个子串所修改的最小字符数+最后一个子串所需要修改字符数。(最后一个子 串就是前i个字符-前m个字符)所以再写一层循环

  for(int m=j-1;m<i;m++)
 {
   dp[i][j] = Math.min(dp[i][j], dp[m][j - 1] + change(s, m, i - 1));
 }

 最后返回dp[len][k]即可

四、完整代码

 public int change(String s,int left,int right){
        int cnt=0;
        while(left<right)
        {
            if(s.charAt(left++)!=s.charAt(right--))
                cnt++;

        }
        return cnt;
    }
    public int palindromePartition(String s, int k) {
        int len=s.length();

        int [][]dp=new int [len+1][k+1];
        for (int i = 0; i < dp.length; i++) {
            Arrays.fill(dp[i], len);
        }
        //前i个数,分割成j个回文字符串
        for(int i=1;i<=len;i++)
        {
            int l=Math.min(i,k);
            for(int j=1;j<=l;j++)
            {
                if(j==1)
                    dp[i][j]=change(s,j-1,i-1);
                else
                {
                    for(int m=j-1;m<i;m++)
                    {
                       dp[i][j] = Math.min(dp[i][j], dp[m][j - 1] + change(s, m, i - 1));
                    }
                }
            }
        }
        return dp[len][k];
    }

 

 

五、可优化部分

        在本题中,除了三层循环耗时较多外,每次都需要计算的change值也占据的大量时间,其中也包含了重复运算。所以我们可以先用一个二维数组把每个字符串修改所需要的次数都记录起来,在主函数使用的时候直接取数组值即可,而不必进行函数运算。

    public int palindromePartition(String s, int k) {
        int len=s.length();
        int [][]palind=new int [len][len];
        for(int j=len-2;j>=0;j--)
        {
            for(int i=j+1;i<len;i++)
            {
                palind[j][i]=palind[j+1][i-1]+(s.charAt(i)==s.charAt(j)?0:1);
            }
        }
        int [][]dp=new int[len+1][k+1];
        for(int i=1;i<dp.length;i++)
            Arrays.fill(dp[i],Integer.MAX_VALUE);
        for(int i=1;i<=len;i++)
        {
            int l=Math.min(i,k);
            for(int j=1;j<=l;j++)
            {
                if(j==1)
                    dp[i][j]=palind[j-1][i-1];
                else
                    for(int m=j-1;m<i;m++)
                    {
                        dp[i][j]=Math.min(dp[i][j],dp[m][j-1]+palind[m][i-1]);
                    }
            }
        }
        return dp[len][k];
    }

六、感想

        没啥说的了,这题好难,我看了老久...每日一题,明天继续。

 

  • 8
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

少๑渊

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值