【LeetCode】动态规划题型刷起来

一维数组的动态和

先从简单开始,没看题解完成简单的动态规划;
给你一个数组 nums 。数组「动态和」的计算公式为:runningSum[i] = sum(nums[0]…nums[i]) 。

请返回 nums 的动态和。

示例 1:

输入:nums = [1,2,3,4]
输出:[1,3,6,10]
解释:动态和计算过程为 [1, 1+2, 1+2+3, 1+2+3+4]

示例 2:

输入:nums = [1,1,1,1,1]
输出:[1,2,3,4,5]
解释:动态和计算过程为 [1, 1+1, 1+1+1, 1+1+1+1, 1+1+1+1+1]

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/running-sum-of-1d-array
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

这道题其实就是累加,想想累加的特性,前几个数的和再加上当前数,这道题将我们每一步算下来的值都保存。

   public int[] runningSum(int[] nums) {
        if(nums==null||nums.length==0){
            return new int[0];
        }
        int len=nums.length;
        int []dp=new int [len];
        dp[0]=nums[0];
        for(int i=1;i<len;i++){
            dp[i]=nums[i]+dp[i-1];
        }
        return dp;

    }

滑动窗口的最大值

这个题也是简单题,坚持不看题解;

给定一个数组 nums 和滑动窗口的大小 k,请找出所有滑动窗口里的最大值。


示例:

输入: nums = [1,3,-1,-3,5,3,6,7], 和 k = 3
输出: [3,3,5,5,6,7] 
解释: 

  滑动窗口的位置                最大值
---------------               -----
[1  3  -1] -3  5  3  6  7       3
 1 [3  -1  -3] 5  3  6  7       3
 1  3 [-1  -3  5] 3  6  7       5
 1  3  -1 [-3  5  3] 6  7       5
 1  3  -1  -3 [5  3  6] 7       6
 1  3  -1  -3  5 [3  6  7]      7

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/hua-dong-chuang-kou-de-zui-da-zhi-lcof
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

这道题其实就是动态规划加双指针;

 public int[] maxSlidingWindow(int[] nums, int k) {
        if(nums==null||nums.length==0){
            return new int[0];
        }
        int len=nums.length;
        int []res=new int[len-k+1];
        for(int i=0;i<len-k+1;i++){
            res[i]=nums[i];
            for(int j=i+1;j<i+k;j++){
                res[i]=Math.max(res[i],nums[j]);
            }
        }
        return res;
    }

回文子串

给定一个字符串,你的任务是计算这个字符串中有多少个回文子串。

具有不同开始位置或结束位置的子串,即使是由相同的字符组成,也会被视作不同的子串。

示例 1:

输入:"abc"
输出:3
解释:三个回文子串: "a", "b", "c"

示例 2:

输入:"aaa"
输出:6
解释:6个回文子串: "a", "a", "a", "aa", "aa", "aaa"

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/palindromic-substrings
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

这道题属于中等范畴

这个题最先想到的办法就是将所有子串都列举出来,然后判断是不是回文子串;说实话,列举所有子串的算法我好像也不会;

看了官方题解:
1、中心扩展:枚举每一个可能的回文中心,然后用两个指针分别向左右两边拓展,当两个指针指向的元素相同时就扩展,否则停;

  public int countSubStrings(String s){
        int n=s.length(),res=0;
        for(int i=0;i<2*n-1;i++){
            int l=i/2,r=i/2+i%2;
            while (l>=0&&r<n&&s.charAt(l)==s.charAt(r)){
                --l;
                ++r;
                ++res;
            }
        }
        return res;
    }

这个中心扩展法很妙;
官方解题的思路,i<2*n-1控制的很妙,规律也找的很妙,记录回文中心;
2、Manacher算法:Manacher算法是在线性时间内求解最长回文子串的算法。

Manacher算法依旧需要枚举s的每一个位置并先假设它是回文中心,但是它会利用已经计算出来的状态来更新f(i),而不是向【中心拓展】一样盲目地扩展,具体地说,假设我们已经计算好了【1,i-1】区间内所有点的f(最大半径),那么我们也就知道了【1,i-1】拓展出的回文达到最大半径时的回文右端点i+f(i)-1.

那么Manacher算法是如何通过已经计算出的状态来更新f(i)呢?
Manacher算法要求我们维护【当前最大的回文的右端点Rm】以及这个右端点所对应的回文中心im,我们需要顺序遍历s,假设当前遍历的下标为i。 我们知道在求解f(i)之前我们应当已经得到了从【i,i-1】所有的f,并且当前已经有了一个最大回文右端点rm以及它对应的回文中心im。
初始化f(i):如果i被包含在当前最大的回文子串内,假设j是i关于这个最大回文的回文中心im的对称位置(即j+1=2*im),我们可以得到f(i)至少等于min{f(i),rm-i+1}。保证这个回文串在当前最大回文串内。
中心扩展:做完初始化后,我们可以保证此时的s【i+f(i)-1】=s【i-f(i)+1】,要继续扩展这个区间,我们就要继续判断s【i+f(i)】和s【i-f(i)】是否相等,如果相等将f(i)自增;这样循环直到s【i+f(i)】不等于s【i-f(i)】。需要注意下标不能越界,这里使用的办法是在开头加一个$结尾加一个!,这样开头和结尾两个字符一定不相等,循环就可以在这里终止。

这样我们就可以得到s所有点为中心的最大回文半径,也能够得到s中所有可能的回文中心的最大回文半径,把它们累加便可。

   public int countSubStrings1(String s){
        int n=s.length();
        StringBuffer t=new StringBuffer("$#");
        for(int i=0;i<n;i++){
            t.append(s.charAt(i));
            t.append('#');
        }
        t.append('!');

        int[] f=new int[t.length()];
        int iMax=0,rMax=0,ans=0;
        for(int i=1;i<t.length();i++){
            f[i]=i<=rMax?Math.min(rMax-i+1,f[2*iMax-1]):1;
            while (t.charAt(i+f[i])==t.charAt(i-f[i])){
                ++f[i];
            }
            //
            if(i+f[i]-1>rMax){
                iMax=i;
                rMax=i+f[i]-1;
            }
            //
            ans+=f[i]/2;
        }
        return ans;
    }

这个算法我还没理解透,完了再看吧;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值