二刷力扣——DP算法(子序列问题)

news2024/7/6 20:01:31

300. 最长递增子序列

定义是以本元素结尾,所以公式初始化都好弄。但是太慢

class Solution {
    public int lengthOfLIS(int[] nums) {
        int n=nums.length;
        int[] dp = new int[n];//以自己结尾的最长递增子序列
        dp[0]=1;
        int maxzi=1;
        for(int i=1;i<n;++i)
        {
            dp[i]=1;
           for(int j=0;j<i;++j)
           {
            if(nums[i]>nums[j])dp[i]=Math.max(dp[i],dp[j]+1);
           } 
           maxzi=Math.max(maxzi,dp[i]);
        }
        return maxzi;
    }
}

时间O(n^2),空间O(n)

看题解的优化:

贪心+二分查找:

下面链接说的很清楚:

https://writings.sh/post/longest-increasing-subsequence-revisited

中心思想是:

7bc063999cfd400986b22078eed5da2b.png

所以这里的d数组定义是:d[i]是长度为 i 的递增子序列的最小结尾元素。比如d[2]=6,所以长度为2 的递增子序列里面,结尾元素最小的情况是2。

所以挨个遍历nums数组来更新这个d数组,注意这个d数组是动态的,不像之前动态规划的DP数组,我们得记录他的长度,因为遇到了nums某元素比d最后的结尾还大,就要把这个某元素加入到d数组,扩大d数组。。如果遇到某元素比结尾小,对应d数组定义,那d数组里面肯定有某个值要更新为这个nums[i],这个值是哪个位置?就是把比nums[i]大的&最接近nums[i]的 换成nums[i]。举例下图。遇到3比d 的结尾8要小,所以更新d  的某个元素,找到比3大的&最接近3 的元素是6,so替换。这样保证d[2]=3,对应长度为2的递增子序列的最小结尾元素是3(2、3这个子序列)。

f9ab93768f754176b843a471d640e040.png

所以在过程中,d数组长度不会减少,所以p数组装的nums元素 的个数,就是所要求的最长递增子序列长度,d数组的最后结尾元素代表了一条递增最慢的子序列,所以这个元素的下标,也就是len就是结果。

还有一个解释:

1cbc84e833f4447cb6d9eebf4c8ec244.png

代码注意,没有写==的时候break,所以退出循环一定是left=right+2了,这个时候left的位置就是 比nums[i]大的&最接近nums[i]的 位置,然后直接替换成nums[i]。

len是为了记录包含nums元素 的个数,因为这里没有使用大小可动态变化的集合。

class Solution {
    public int lengthOfLIS(int[] nums) {
        int n=nums.length;
        if(n==0)return 0;
        int len =1;
        int[] d = new int[n+1];//长度为i的递增子序列的最小结尾元素
        d[len]=nums[0];
        for(int i=1;i<n;++i)
        {
            if(nums[i]>d[len])//加到结尾去
            {
                d[++len]=nums[i];
            }
            else//在d[1]-d[len]中找到更改为nums[i]后使得d仍然有序的位置;二分查找更快
            {
                int l=1,r=len;//左闭右闭
                while(l<=r)
                {
                    int mid=(l+r)>>1;
                    if(d[mid]<nums[i]){
                        l=mid+1;
                    }
                    else r=mid-1;
                }
                d[l]=nums[i];//替换
            }
        }
        return len;
    }
}

贪心遍历nums,动态规划遍历dp,给固定大小的数组的每个元素求解值。

674. 最长连续递增序列

可以DP数组,也可以贪心省存储。

贪心:用start记录连续子序列的开头下标,发现连续递增在i 这里断了,就更新start为i(重新一个递增序列)。然后取最大值

class Solution {
    public int findLengthOfLCIS(int[] nums) {
        int n=nums.length,maxCount=1;
        int start=0;
        for(int i=1;i<n;++i)
        {
            if(nums[i]<=nums[i-1])
            {
                start=i;
            }
            maxCount=Math.max(maxCount,i-start+1);
        }
        return maxCount; 
    }
}

718. 最长重复子数组

dp[i][j]:如果不想单独初始化的话,dp设置比nums偏移一个坐标,是以nums1[i-1]和nums2[j-1]结尾的子数组最长 长度;不偏移的话是nums1[i]和nums2[j]结尾的子数组最长 长度

公式:如果遍历的两个元素相等,可以在上一个dp[i-1][j-1]的基础上拼接这个元素,所以+1

偏移:

class Solution {
    public int findLength(int[] nums1, int[] nums2) {
        int m=nums1.length,n=nums2.length;
        int[][] dp=new int[m+1][n+1];//以nums1[i-1]和nums2[j-1]结尾的子数组最长 长度
        int maxCount=0;
        for(int i=1;i<=m;++i)
        {
            for(int j=1;j<=n;++j)
            {
                if(nums2[j-1]==nums1[i-1])
                {
                    dp[i][j]=dp[i-1][j-1]+1;
                    maxCount=Math.max(dp[i][j],maxCount);
                }
            }
        }
        return maxCount;
    }
}

还可以用滚动数组实现,注意跟 01背包的滚动数组实现 一样,留下的那个维度只能在内循环,而且得从大到小,因为得是上一轮的值而不是这一轮更新过的值。

而且不相等的时候,要把dp[j]清零,因为二维的时候不操作dp[i][[j]直接就是0;但是一维的时候不操作是继承了上一轮的值,是错的。只要不相等以nums1[i-1]结尾的子数组长度就是0,所以置零。

class Solution {
    public int findLength(int[] nums1, int[] nums2) {
        int m=nums1.length,n=nums2.length;
        int[] dp=new int[m+1];//以nums1[i-1] 结尾的子数组最长 长度
        int maxCount=0;
        for(int i=1;i<=n;++i)
        {
            for(int j=m;j>=1;--j)//留下维度从大到小
            {
                if(nums2[i-1]==nums1[j-1])
                {
                    dp[j]=dp[j-1]+1;
                    maxCount=Math.max(dp[j],maxCount);
                }
                else dp[j]=0;   //不相等了,清零
            }
        }
        return maxCount;
    }
}

不偏移:

class Solution {
    public int findLength(int[] nums1, int[] nums2) {
        int m=nums1.length,n=nums2.length;
        int[][] dp=new int[m ][n ];//nums1[i]和nums2[j]结尾的子数组最长 长度
        int maxCount=0;
        for(int i=0;i<n;++i){
            dp[0][i]=nums1[0]==nums2[i]?1:0;
            maxCount=Math.max(dp[0][i],maxCount);
        }
        for(int i=0;i<m;++i){
            dp[i][0]=nums1[i]==nums2[0]?1:0;
            maxCount=Math.max(dp[i][0],maxCount);
        }
        for(int i=1;i<m;++i)
        {
            for(int j=1;j<n;++j)
            {
                if(nums2[j]==nums1[i])
                {
                    dp[i][j]=dp[i-1][j-1]+1;
                    maxCount=Math.max(dp[i][j],maxCount);
                }
            }
        }
        return maxCount;
    }
}

1143. 最长公共子序列

dp[i][j]的定义跟上面不一样,这里子序列是不连续的,如果定义还要求是以元素结尾的子序列长度的话,i元素=j元素,dp[i][j]不一定是在dp[i-1][j-1]的基础上+1,就会很麻烦。

所以设置为:text1[0,i-1]和text2[0,j-1]范围内,最长公共子序列的长度.

最后返回的就是dp[m][n]

递推公式:如果相等的话,是在dp[i-1][j-1]的基础上+1。

如果不相等,不能直接跳过,比如abcde,ace,到了abc、ace的时候c!=e,那么dp[3][3]按照定义应该是2,等于dp[3][2]。把abc、ace倒过来,dp[3][3]又=dp[2][3]。A[i-1]和B[j-1]不相等,但是A[i-1]可能和B[j-1]之前的相等,B[j-1]可能和A[i-1]之前的相等,所以要取这两种情况的最大值。

采用跟上提一样的偏移写法:

class Solution {
    public int longestCommonSubsequence(String text1, String text2) {

        int m=text1.length(),n=text2.length();
        int[][] dp=new int[m+1][n+1];//text1[0,i-1]和text2[0,j-1]范围内,最长公共子序列的长度
        for(int i=1;i<=m;++i)
        {
            for(int j=1;j<=n;++j)
            {
                if(text1.charAt(i-1)==text2.charAt(j-1))
                {
                    dp[i][j]=dp[i-1][j-1]+1;
                }
                else dp[i][j]=Math.max(dp[i-1][j],dp[i][j-1]);
            }
        }
        return dp[m][n];
    }
}

同样可以用滚动数组吗?

不行,

c1b233ccbae44bc3b7a8e21d12deb35c.png

dp[i][j]取决于这三个元素,如果用滚动数组,无论行优先,还是列优先遍历,dp[i][j-1]/dp[i-1][j]都会把dp[i][j]覆盖。

1035. 不相交的线

跟上题一样。

392. 判断子序列

编辑距离问题 的入门题目。用上面的方法然后判断dp[m][n]是否=s.length()的话,效率低。

所以也可以用双指针做一下:

class Solution {
    public boolean isSubsequence(String s, String t) {
        int m=s.length(),n=t.length();
        int sp=0,tp=0;
        while(sp<m && tp<n)//sp:s已经匹配了的右边界
        {
            
            if(s.charAt(sp)==t.charAt(tp))
            {
                sp++;
            }
            tp++;
        }
        if(sp==m)return true;
        return false;
    }
}

时间O(n)。

有一个进阶问题;

95c0ade49a9d4c50a960ee0a0522aa79.png

力扣题解给出一个跟S 无关的预处理DP数组的做法:dp[i][j] 表示字符串 t 中从位置 i 开始往后字符 j 第一次出现的位置。

递推公式:

e688bf5962aa4d4783628f4c9783d233.png

初始化:有效的下标 i 就是0到n-1,所以多设置一个n ,让dp[n][j]都=n,代表位置不存在。这是判断false的条件。

遍历顺序:得到下标 i到n-1 的第一次位置,所以i 需要从大到小。

class Solution {
    public boolean isSubsequence(String s, String t) {
        int m=s.length(),n=t.length();
        int[][] dp=new int[n+1][26];//字符串 t 中从位置 i 开始往后字符 j 第一次出现的位置
        //初始化
        for(int i=0;i<26;++i)
        {
            dp[n][i]=n;//代表不存在
        }
        for(int i=n-1;i>=0;--i)//和s无关,得到dp数组
        {
            for(int j=0;j<26;++j)
            {
                if(t.charAt(i)==j+'a')
                {
                    dp[i][j]=i;//更新位置
                }
                else dp[i][j]=dp[i+1][j];
            }
        }
        //检查S1、S2……
        int add=-1;
        for(int i=0;i<m;++i)
        {
            add=dp[add+1][s.charAt(i)-'a'];//以上一次找到的位置的下一个为起点
            System.out.println(add);
            if(add==n)return false;
        }
        return true;
    }
}

过程大概就是:

s = "abc", t = "ahbgdc"

s[0]=a在t[0,n-1]的第一个位置, 是0;

s[1]=b在t[1,n-1]的第一个位置,是2;

s[2]=c在t[3,n-1]的第一个位置,是5;

保证了s各字母在t中找到的位置是递增的,而且这个位置不等于 代表不存在的n,就返回true。

115. 不同的子序列

仔细看题目,在s子序列t 出现的个数。s的子序列是不连续的,t是整个连续的。

定义dp的时候,dp[i][j]表示 在s[0-i](不一定以i结尾)中t[0,j](连续,一定以j结尾)出现的个数。

所以这里dp[i][j]有两种可能:以s[i]结尾和不以s[i]结尾,这是分情况讨论的来源。

1、当s[i-1]==t[j-1],以s[i-1]结尾的话,t[j-1]也定了,所以只能等于不包含这两个相同元素的个数,也就是dp[i-1][j-1];

不以s[i-1]结尾的话,那就是不包含这个元素,所以要找这个元素之前子序列出现个数,即dp[i-1][j]。

递推公式题解中的解释,力扣给的逆序的DP:

e4033dc16c494731b0336577a235b0ff.png

2、当s[i-1]!=t[j-1],肯定只会是不以s[i-1]结尾了,所以让s[0,i-2]和t[0,j-1]匹配,即dp[i-1][j]。

初始化:如果像上面的那样,初始化为0,结果只会是0。先看dp[i][0],s[0-i-1]包含空串的数量,应该为1;而且空串也是空串的子串,所以dp[0][0]=1。dp[0][j],空串包含t[0,j-1]的数量,应该是0。

class Solution {
    public int numDistinct(String s, String t) {
        int m=s.length(),n=t.length();
        int[][] dp=new int[m+1][n+1];//在s[0-i-1](不要求以i结尾)的子序列中t[0,j-1](要以j结尾)出现的个数
        for(int i=0;i<=m;++i)dp[i][0]=1;
        for(int i=1;i<=m;++i)
        {
            for(int j=1;j<=n;++j)
            {
                if(s.charAt(i-1)==t.charAt(j-1))dp[i][j]=dp[i-1][j ]+dp[i-1][j-1];
                else dp[i][j]=dp[i-1][j];
            }
        }
        return dp[m][n];
    }
}

用Java不用取余也能过。

也可以用滚动数组。注意内循环也得从大到小:

fe33b2df35b84cf2a5f6d350ca31fd96.png

class Solution {
    public int numDistinct(String s, String t) {
        int m=s.length(),n=t.length();
        int[] dp=new int[n+1];//在s[0-i-1](不要求以i结尾)的子序列中t[0,j-1](要以j结尾)出现的个数
        dp[0]=1;
        for(int i=1;i<=m;++i)
        {
            for(int j=n;j>=1;--j)//避免覆盖上一轮的
            {
                if(s.charAt(i-1)==t.charAt(j-1))dp[j]+=dp[j-1];
                // else dp[j]=dp[j];
            }
        }
        return dp[n];
    }
}

583. 两个字符串的删除操作

dp[i][j]定义:1[0—i]、2[0—j ]相等的最小步数。

初始化:根据定义,dp[0][i]应该=i;同理dp[0][j]=j。

2元素相等,那肯定这一步不用删除元素,直接等于之前的dp[i-1][j-1]

2个元素不相等的时候,需要删除,那就有三种情况。删一个有两种,或者两个都删。既然是最小步数,所以取最小值。

class Solution {
    public int minDistance(String word1, String word2) {
        int m=word1.length(),n=word2.length();
        int[][] dp=new int[m+1][n+1];//1[0,i-1]、2[0,j-1]相等的最小步数
        for (int i = 1; i <= m; i++)dp[i][0] = i;
        for (int j = 1; j <= n; j++)dp[0][j] = j;
        for(int i=1;i<=m;++i)
        {
            for(int j=1;j<=n;++j)
            {
                if(word1.charAt(i-1)==word2.charAt(j-1))
                {
                    dp[i][j]=dp[i-1][j-1];
                }
                else dp[i][j]=Math.min(Math.min(dp[i-1][j],dp[i][j-1])+1,dp[i-1][j-1]+2);
            }
        }
        return dp[m][n];
    }
}

也不能滚动数组。

最后,综合题:

72. 编辑距离

相等和上面是一样的。

不相等:(注意只能操作word1)

增:dp[i][j-1]+1。增了之后新增的和2[j-1]相等。

删:dp[i-1][j]+1

换:dp[i-1][j-1]+1

关于增删情况的解释:

eb1d38ec3cf44a39b2386f20f798d41b.png

所以取这三个的最小值。

class Solution {
    public int minDistance(String word1, String word2) {
        int m=word1.length(),n=word2.length();
        int[][] dp=new int[m+1][n+1];//1[0,i-1]转化成2[0,j-1]
        for(int i=1;i<=m;++i)dp[i][0]=i;
        for(int j=1;j<=n;++j)dp[0][j]=j;
        for(int i=1;i<=m;++i)
        {
            for(int j=1;j<=n;++j)
            {
                if(word1.charAt(i-1)==word2.charAt(j-1))
                {
                    dp[i][j]=dp[i-1][j-1];
                }
                else {
                    dp[i][j]=1+Math.min(dp[i-1][j-1],Math.min(dp[i][j-1],dp[i-1][j]));
                }
            }
        }
        return dp[m][n];
    }
}

编辑距离总结篇

dp大小一般都设置[m+1][n-1]。dp[i][j]代表1[i-1]和2[j-1] 的关系。是有意义的,比如1、2是字符串的话,dp[0]就涉及到空串。

1、判断子序列:可以DP,可以双指针。

2、不同的子序列:比较难,要把握题目意思,两种情况都要考虑(有/无 s子序列结尾元素)。有的情况是dp几,无的情况是dp几。

b73eaae55aba4746a7a56be7425e1f03.png

有的话是dp[i-1][j-1]。因为i-1和j-1能对上,所以这两个固定了,就不考虑了,只看前面的个数。

无的话是dp[i-1][j],只能确定不考虑i-1,考虑i-2及之前的;j-1不知道和谁对上,所以j-1仍然要考虑,即为dp[i-1][j]

这里的思想和编辑距离是差不多的。

3、两个字符串删除操作

不相等的时候,有三种情况,算好做一点。

3c8723d309504ca196d899e9371461d8.png

4、编辑距离

有了前面的铺垫好做一些了。

27586126b5cc4daeaf5ab94254085cfd.png

要想清楚的仍是 删/增/改 分别对应dp几:

b229fd119be84201967861e2ea53510d.png

子数组,子序列问题,dp的定义要思考,要不要以当前元素结尾?结尾的话好递推吗?…

647. 回文子串

这里dp定义是:[i,j]范围内是回文子串,为true。

如果定义是数量的话,dp[i][j]只知道i、j 包含内的子串数目,不能像两端更大范围的序列传达自己是不是回文子串,递推公式也不方便弄,所以定义dp[i][j]就为bool型。

回文子串涉及到两端,如果i 和 j  相同,同时 里面的 子串[i+1,j-1]是回文的(DP数组记录),就能确定这是回文子串,count++。

特殊的,i+1>j-1,那么①里面的子串就没有,【i,j】就两个字母,当然也是回文子串。②i==j,单个字母也是回文子串:

class Solution {
    public int countSubstrings(String s) {
        int n=s.length();
        boolean[][] dp=new boolean[n][n];//[i,j]是不是回文子串
        int count=0;//统计
        for(int i=n-1;i>=0;--i)
        {
            for(int j=i;j<n;++j)
            {
                if(s.charAt(i)==s.charAt(j)){
                    if(j<=i+1||dp[i+1][j-1]==true)
                    {
                        dp[i][j]=true;
                        count++;
                        System.out.println(i+"  "+j);
                    }
                }
            }
        }
        return count;
    }
}

注意要先得到dp[i+1][j-1],所以i从后往前。

时间、空间O(n2);稍微有点慢。

2、力扣题解的中心扩展法:

每个回文,回文中心有 偶数个 和 奇数个 两种情况。也就是最中间是两个,还是一个。

所以遍历所有的回文中心(i,j),指针扩展(l,r),然后统计:

class Solution {
    public int countSubstrings(String s) {
        int n=s.length();
        int count=0;
        for(int i=0;i<n;++i)
        {
            for(int j=0;j<2;++j)
            {
                //确定回文中心左右起点
                int l=i;
                int r=i+j;
                while(l>=0 && r<n && s.charAt(l--)==s.charAt(r++))
                {
                    count++;
                }
            }
        }
        return count;
    }
}

空间O(1)

516. 最长回文子序列

这里是回文子序列,不连续的

dp可以用长度表示了,

class Solution {
    public int longestPalindromeSubseq(String s) {
        int n=s.length();
        int maxLength=1;
        int[][] dp=new int[n][n];//[i,j]回文子序列的长度
        for(int i=n-1;i>=0;--i)
        {
            dp[i][i]=1;//必须
            for(int j=i+1;j<n;++j)
            {
                if(s.charAt(i)==s.charAt(j))
                {
                    dp[i][j]=dp[i+1][j-1]+2;
                }
                else
                {
                    dp[i][j]=Math.max(dp[i+1][j],dp[i][j-1]);
                }
                maxLength=Math.max(dp[i][j],maxLength);
            }
        }
        return maxLength;
    }
}

递推公式和上面的思想一致,注意的是2个特殊情况(i+1>=j-1,i到j只有一个或者2个元素):

①i==j:需要写出来dp[i][j]=1。

②i+1<=j:+1==j的时候dp[i][j]=2。不用分开写出,因为dp[i+1][j-1]是左下角的元素,左下角都为0而且不会更新,所以<=的时候都可以用dp[i][j]=dp[i+1][j-1]+2;得到。

不相等的时候,最长回文子序列要么不包含s[i],要么不包含s[j],所以有两种情况:不考虑s[i]或不考虑s[j]。取两个的最大值即可:dp[i][j]=max(dp[i][j-1],dp[i+1][j]);

也不能滚动数组优化,

dp[i][j]

因为dp[i][j]靠这三个得到

动态规划总结

递归五部曲:

  1. 确定dp数组(dp table)以及下标的含义

一维还是二维,能不能滚动数组实现

  1. 确定递推公式

分类讨论所有情况

  1. dp数组如何初始化

有时候根据DP含义,有时候为了不影响后面元素的更新,比如背包里面求最大,初始元素就得为0,求最小,初始元素就得为INT_MAX。

  1. 确定遍历顺序

主要根据递推公式的左右式,dp[i][j]根据谁来,遍历顺序就要满足这个先完成更新。

  1. 举例推导dp数组

动态规划基础

  • 关于动态规划,你该了解这些!
  • (opens new window)
  • 动态规划:斐波那契数
  • (opens new window)
  • 动态规划:爬楼梯
  • (opens new window)
  • 动态规划:使用最小花费爬楼梯
  • (opens new window)
  • 动态规划:不同路径
  • (opens new window)
  • 动态规划:不同路径还不够,要有障碍!
  • (opens new window)
  • 动态规划:整数拆分,你要怎么拆?
  • (opens new window)
  • 动态规划:不同的二叉搜索树

 

这里整数拆分和二叉搜索树有点忘了。

实际上都是的把dp[i]分成两部分来看,整数拆分先拆分两个,固定一端再看另一端是再拆分大(dp[i-j])还是i-j大。

二叉搜索树是分别看左右子树,对dp[i],左子树有0到i-1个 的可能,右子树相对应是i-1到0个 的可能,乘法法则,应该dp[i]=∑(j=0到i-1)dp[j]*dp[i-1-j]。

背包问题系列

动态规划:关于01背包问题,你该了解这些!

  • (opens new window)
  • 动态规划:关于01背包问题,你该了解这些!(滚动数组)
  • (opens new window)
  • 动态规划:分割等和子集可以用01背包!
  • (opens new window)
  • 动态规划:最后一块石头的重量 II
  • (opens new window)
  • 动态规划:目标和!
  • (opens new window)
  • 动态规划:一和零!
  • (opens new window)
  • 动态规划:关于完全背包,你该了解这些!
  • (opens new window)
  • 动态规划:给你一些零钱,你要怎么凑?
  • (opens new window)
  • 动态规划:Carl称它为排列总和!
  • (opens new window)
  • 动态规划:以前我没得选,现在我选择再爬一次!
  • (opens new window)
  • 动态规划: 给我个机会,我再兑换一次零钱
  • (opens new window)
  • 动态规划:一样的套路,再求一次完全平方数
  • (opens new window)
  • 动态规划:单词拆分
  • (opens new window)
  • 动态规划:关于多重背包,你该了解这些!
  • (opens new window)
  • 听说背包问题很难? 这篇总结篇来拯救你了

题目很多,主要讲了01背包和完全背包。

01背包递推公式考虑放不放当前物品i两种情况,二维(物品、背包重量0到weight)和一维(背包重量)的遍历顺序不一样。有些题要知道怎么转换成背包问题。

完全背包一个物品可以放无限次,之前01背包滚动数组的内循环得是从大到小,否则是重复放物品多次,但是这满足了完全背包的条件,多以是从小到大。关于先物品还是先背包,排列是先背包,组合是先物品。

打家劫舍系列

  • 动态规划:开始打家劫舍!
  • (opens new window)
  • 动态规划:继续打家劫舍!
  • (opens new window)
  • 动态规划:还要打家劫舍!

第二个是第一个的扩展,连成圈的话要把dp数组的过程封装起来,有一些特殊情况(n-1和n=2)不用考虑圈可以直接返回,封装的函数注意统一边界的闭合。

第三个是树形DP,每个节点都要考虑选和不选两种情况,而且要记录下来,给求得父节点的值提供选择。所以每个节点都维护一个数组(里面两个元素 )。总的相当于二维DP数组

股票系列

动态规划:买卖股票的最佳时机

  • (opens new window)
  • 动态规划:本周我们都讲了这些(系列六)
  • (opens new window)
  • 动态规划:买卖股票的最佳时机II
  • (opens new window)
  • 动态规划:买卖股票的最佳时机III
  • (opens new window)
  • 动态规划:买卖股票的最佳时机IV
  • (opens new window)
  • 动态规划:最佳买卖股票时机含冷冻期
  • (opens new window)
  • 动态规划:本周我们都讲了这些(系列七)
  • (opens new window)
  • 动态规划:买卖股票的最佳时机含手续费
  • (opens new window)
  • 动态规划:股票系列总结篇
  • (opens new window)

要确定好dp数组的第二个维度,也就是每天会有多少种状态。然后分情况 每种状态的 前一天有哪几种状态,从而确定递推公式。

子序列系列

动态规划:最长递增子序列

  • (opens new window)
  • 动态规划:最长连续递增序列
  • (opens new window)
  • 动态规划:最长重复子数组
  • (opens new window)
  • 动态规划:最长公共子序列
  • (opens new window)
  • 动态规划:不相交的线
  • (opens new window)
  • 动态规划:最大子序和
  • (opens new window)
  • 动态规划:判断子序列
  • (opens new window)
  • 动态规划:不同的子序列
  • (opens new window)
  • 动态规划:两个字符串的删除操作
  • (opens new window)
  • 动态规划:编辑距离
  • (opens new window)
  • 为了绝杀编辑距离,我做了三步铺垫,你都知道么?
  • (opens new window)
  • 动态规划:回文子串
  • (opens new window)
  • 动态规划:最长回文子序列
  • (opens new window)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1891206.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

开源自动化热键映射工具autohotkey十大用法及精选脚本

AutoHotkey&#xff08;AHK&#xff09;是一款功能强大的热键脚本语言工具&#xff0c;它允许用户通过编写脚本来自动化键盘、鼠标等设备的操作&#xff0c;从而极大地提高工作效率。以下是AutoHotkey的十大经典用法&#xff0c;这些用法不仅解放了用户的双手&#xff0c;还展示…

(十二)纹理和采样

纹理 在绘制三角形的过程中&#xff0c;将图片贴到三角形上进行显示的过程&#xff0c;就是纹理贴图的过程 uv坐标 如果如果图片尺寸和实际贴图尺寸不一致&#xff0c;就会导致像素不够用了的问题 纹理与采样 纹理对象(Texture)&#xff1a;在GPU端&#xff0c;用来以一…

RPA 第一课

RPA 是 Robotic Process Automation 的简称&#xff0c;意思是「机器人流程自动化」。 顾名思义&#xff0c;它是一种以机器人&#xff08;软件&#xff09;来替代人&#xff0c;实现重复工作自动化的工具。 首先要说一句&#xff0c;RPA 不是 ChatGPT 出来之后的产物&#x…

新火种AI|国产大模型展开决战,是资本游戏还是技术革命?

作者&#xff1a;一号 编辑&#xff1a;美美 资本角逐与技术革新&#xff0c;国产大模型的双线战场已然开启。 随着人工智能技术的不断进步&#xff0c;国产大模型正迅速成为行业关注的焦点。在这个由数据驱动的时代&#xff0c;资本的注入和技术创新的加速&#xff0c;让国…

基于Teager-Kaiser能量算子的肌电信号降噪方法(MATLAB)

Teager-Kaiser能量算子是一种非线性算子&#xff0c;它能有效提取信号的瞬时能量&#xff0c;对信号瞬时变化具有良好的时间分辨率。Teager-Kaiser能量算子只需信号三个采样点&#xff0c;即可快速跟踪信号的幅值和角频率变化&#xff0c;计算实现简单、运算量小。 clc clear a…

excel表格如何换行,这几个操作方法要收藏好

Excel表格作为一款强大的数据处理工具&#xff0c;在日常工作和生活中被广泛应用。当需要在单元格内显示较长的文本内容或使数据更加清晰易读时&#xff0c;我们需要掌握一些换行技巧。下面将介绍几种常用的Excel换行方法&#xff1a; 一、使用快捷键换行 1、首先&#xff0c;…

SpringBoot+Vue集成AOP系统日志

新建logs表 添加aop依赖 <!-- aop依赖--> <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId> </dependency> 新建获取ip地址工具类 import javax.servlet.http.H…

java常用类(3)

目录 一. 正则表达式 二. Math类 三. Random类 四. Date类 五. Calendar类 六. SimpDateFormate类 七. BigInteger类 八. BigDecimal类 一. 正则表达式 正则表达式(Regular Expression)就是用一些特殊的符号去匹配一个字符串是否符合规则,利用String类中的matches()方…

离线查询+线段树,CF522D - Closest Equals

一、题目 1、题目描述 2、输入输出 2.1输入 2.2输出 3、原题链接 522D - Closest Equals 二、解题报告 1、思路分析 考虑查询区间已经给出&#xff0c;我们可以离线查询 对于这类区间离线查询的问题我们通常可以通过左端点排序&#xff0c;然后遍历询问同时维护左区间信息…

用机器改变人类方向

1800 世纪初&#xff0c;美国迎来了工业革命&#xff0c;这是一个由技术进步推动的变革时代。新机器和制造技术的引入重塑了经济格局&#xff0c;提高了生产效率&#xff0c;同时减少了某些领域对手工劳动的需求。因此&#xff0c;这种转变导致了失业。 如今&#xff0c;我们看…

【漏洞复现】朗新智能人力资源系统(HCM) GetFunc_code.asmx接口处存在SQL注入漏洞

免责声明&#xff1a;文章来源互联网收集整理&#xff0c;请勿利用文章内的相关技术从事非法测试&#xff0c;由于传播、利用此文所提供的信息或者工具而造成的任何直接或者间接的后果及损失&#xff0c;均由使用者本人负责&#xff0c;所产生的一切不良后果与文章作者无关。该…

产线AGV和仓储AGV到底有什么不同?

agv AGV小车虽然体积小巧&#xff0c;但这并不影响它强大的负重能力&#xff0c;它不需要人工去操作驾驶&#xff0c;能够实现无人搬运车的功能&#xff0c;而且随着AGV小车的发展&#xff0c;已经从最传统普遍的磁导航升级为惯性导引和激光导引AGV小车了&#xff0c;从需要在企…

2. Python+Playwright playwright的API

Playwright支持同步和异步两种API&#xff0c;使用异步API需要导入asyncio库&#xff0c;它是一个可以用来实现Python协程的库&#xff0c;更详细介绍可参考Python协程 。我们可以根据自己的偏好选择适合的模式。 同步与异步模式原理 同步操作方式&#xff1a;在代码执行时&am…

【目标检测】DINO

一、引言 论文&#xff1a; DINO: DETR with Improved DeNoising Anchor Boxes for End-to-End Object Detection 作者&#xff1a; IDEA 代码&#xff1a; DINO 注意&#xff1a; 该算法是在Deformable DETR、DAB-DETR、DN-DETR基础上的改进&#xff0c;在学习该算法前&#…

黑马点评-Redis的缓存击穿,缓存雪崩,缓存穿透,互斥锁,逻辑过期

文章目录 1.缓存穿透2.缓存雪崩3.缓存击穿3.1 互斥锁3.2 基于逻辑过期 1.缓存穿透 解决办法 写入NULL值到Redis缓存&#xff0c;以后就会命中Redis的控制缓存而不会出现请求直接打到数据库的问题&#xff01; 代码 2.缓存雪崩 这个概念很好理解&#xff0c;雪崩就是无数的…

复旦大学:一个小技巧探测大模型的知识边界,有效消除幻觉

孔子说“知之为知之&#xff0c;不知为不知&#xff0c;是知也”&#xff0c;目前的大模型非常缺乏这个能力。虽然大模型拥有丰富的知识&#xff0c;但它仍然缺乏对自己知识储备的正确判断。近年来LLMs虽然展现了强大的能力&#xff0c;但它们偶尔产生的内容捏造&#xff0c;即…

240703_昇思学习打卡-Day15-K近邻算法实现红酒聚类

KNN(K近邻)算法实现红酒聚类 K近邻算法&#xff0c;是有监督学习中的分类算法&#xff0c;可以用于分类和回归&#xff0c;本篇主要讲解其在分类上的用途。 文章目录 KNN(K近邻)算法实现红酒聚类算法原理数据下载数据读取与处理模型构建--计算距离模型预测 算法原理 KNN算法虽…

AIGC到底如何改变创意设计?

在当今数字化时代&#xff0c;AIGC&#xff08;生成式人工智能&#xff09;技术的崛起对创意设计领域产生了深远的影响。AIGC不仅为设计师提供了新的工具和方法&#xff0c;还改变了传统的设计流程和思维方式。 传统的设计过程中&#xff0c;设计师需要耗费大量时间在绘图、修…

利用GPT 将 matlab 内置 bwlookup 函数转C

最近业务需要将 matlab中bwlookup 的转C 这个函数没有现成的m文件参考&#xff0c;内置已经打成库了&#xff0c;所以没有参考源代码 但是它的解释还是很清楚的&#xff0c;可以根据这个来写 Nonlinear filtering using lookup tables - MATLAB bwlookup - MathWorks 中国 A…

甘肃黄米粽子:香甜软糯的塞上美食

甘肃黄米粽子是甘肃地区具有特色的传统美食。黄米粽子选用优质的黄米作为主要原料&#xff0c;黄米相较于糯米&#xff0c;有着独特的谷物香气和口感。在制作过程中&#xff0c;将黄米浸泡一段时间&#xff0c;使其充分吸收水分&#xff0c;变得饱满。馅料方面&#xff0c;通常…