打算这段时间(我也不清楚具体多久,打算多刷点动态规划的问题)
动态规划
2022.9.29
最长回文子串
给你一个字符串 s,找到 s 中最长的回文子串。
示例 1:
输入:s = “babad”
输出:“bab”
解释:“aba” 同样是符合题意的答案。
1.中心扩散法:
先写代码
class Solution {
public String longestPalindrome(String s) {
if(s.length()==0||s.length()==1){
return s;
}
else{
int left ;
int right;
int len = 0;
int max = 0;
int start = 0;
int end = 0;
int i ;
for( i = 0;i<s.length();i++){
left = i-1;
right = i+1;
while(left>=0&&(s.charAt(left)==s.charAt(i))){
left--;
len++;
}
while(right<s.length()&&(s.charAt(right)==s.charAt(i))){
right++;
len++;
}
while(left>=0&&right<s.length()&&(s.charAt(left)==s.charAt(right))){
len+=2;
right++;
left--;
}
if(max < len){
max = len;
start = left;
end = right;
}
len = 1;
}
return s.substring(start+1,start+max+1);
}
}
}
我感觉要先说两个api,java好久没写过算法了,忘的差不多了
a.charAt(n)
字符串a中,找到它的第n+1个字符
a.substring(b,c)
只要字符串a中的第b+1个字符到c个字符
然后感觉,与其叫从中心扩散还不如叫暴力枚举法
定义一个i=0,让i的长度小于字符串的长度的循环,每进行一次循环i的大小+1
定义一个left和right,在每次循环的初始分别在i的两侧
后面感觉很容易,没必要写了
2.动态规划:
先写代码
class Solution {
public String longestPalindrome(String s) {
if (s == null || s.length() < 2) {
return s;
}
else{
int start = 0;
int max = 1 ;
boolean dp[][] = new boolean[s.length()][s.length()];
for(int i =1;i<s.length();i++){
for(int j = 0 ;j<i;j++){
if((s.charAt(i)==(s.charAt(j)))&&((i-j<=2)||(dp[j+1][i-1]))){
dp[j][i]=true;
int len = i-j+1;
if(len>max){
max = len ;
start = j;
}
}
}
}
return s.substring(start,start+max);
}
}
}
当时候把boolean dp[][] = new boolean[s.length()][s.length()]
写成
Boolean dp[][] = new Boolean[s.length()][s.length()]
然后报错了,还能过几个样例
dp是经典的用空间换时间
和上一个方法一对比就明感觉就讲一下第一个if里面的语句就行了
==就不用说了,说并后面的
i-j小于等于2这个好理解
dp[j+1][i-1]这句
比如ababa
已经知道ababa是最长回文串,那你就得证明第一个和最后一个相等且bab是回文串
就能理解了
2022.10.18(10.20补上这道题)
最长有效括号
给你一个只包含 ‘(’ 和 ‘)’ 的字符串,找出最长有效(格式正确且连续)括号子串的长度。
示例 1:
输入:s = “(()”
输出:2
解释:最长有效括号子串是 “()”
示例 2:输入:s = “)()())”
输出:4
解释:最长有效括号子串是 “()()”
示例 3:输入:s = “”
输出:0
1.动态规划:
先写代码
class Solution {
public int longestValidParentheses(String s) {
if(s.length()<=1){
return 0;
}
else{
int dp[] = new int[s.length()];
int max = 0;
for(int i=1;i<s.length();i++){
if(s.charAt(i)==')'){
if(s.charAt(i-1)=='('){
dp[i]=(i-2>=0?dp[i-2]:0)+2;
}
else if (i - dp[i - 1] > 0 && s.charAt(i - dp[i - 1] - 1) == '(') {
dp[i] = dp[i - 1] + ((i - dp[i - 1]) >= 2 ? dp[i - dp[i - 1] - 2] : 0) + 2;
}
max = Math.max(max, dp[i]);
}
}
return max;
}
}
}
首先这道题,我们要想有哪几种情况会有比较长的括号
第一种:
()()()()
这种情况是最简单的一种情况
我们不妨从i=1开始(这样简单的多,i=0的情况太难了)
因为是从i=1开始,所以s.charAt(i)必须得用’)’
在s.charAt(i)==')'的情况下,如何得到max呢
就必须s.charAt(i-1)==‘(’
把刚才的情况写一下
012345
( ) ( ) ( )
在s.charAt(i-1)=='('的情况下,如果i-2<0,则dp[i]=0+2
如果i-2>=0,则dp[i]=(dp[i-2]+2)
第二种:
((()))
这种情况比较难
还是从i=1开始
最开始new的时候全部都是0
0 1 2 3 4 5
( ( ( ) ) )
0 0 0 0 0 0
到i=3的时候,dp[3]=dp[1]+2 = 0+2 = 2
但是呢,当i=4的时候,dp[3]=')'而不是(
所以dp[4]仍然为0吗,nonono
所以要改变条件为
若i-dp[i-1]-1==‘(’
依然用上面的这个例子:
0 1 2 3 4 5
( ( ( ) ) )
0 0 0 2 ? ?
i=4时,s.charAt(i-dp[3]-1)==‘(’
所以i=4的**‘)‘可以和i=1的’(’**匹配
原本我以为条件就是这个,但是呢,如果出现这种情况:
0 1 2
( ) )
0 2 ?
当i=2的时候,i-dp[1]-1=2 - 2- 1 =-1
在 else if中会直接报错
所以前面必须再加一个条件 i-dp[i-1]-1>=0
综合得出第二个条件
(i - dp[i-1]-1>=0)&&(s.charAt(i-dp[i-1]-1)==‘(’)
条件是得出来了,dp应该怎么算呢?
首先,一定是和dp[i-1]有关
dp[i] = dp[i - 1] + ((i - dp[i - 1]) >= 2 ? dp[i - dp[i - 1] - 2] : 0) + 2;
2022.10.19(10.21补上这道题)
跳跃游戏
给定一个非负整数数组
nums
,你最初位于数组的 第一个下标 。数组中的每个元素代表你在该位置可以跳跃的最大长度。
判断你是否能够到达最后一个下标。
示例 1:
输入:nums = [2,3,1,1,4]
输出:true
解释:可以先跳 1 步,从下标 0 到达下标 1, 然后再从下标 1 跳 3 步到达最后一个下标。
示例 2:输入:nums = [3,2,1,0,4]
输出:false
解释:无论怎样,总会到达下标为 3 的位置。但该下标的最大跳跃长度是 0 , 所以永远不可能到达最后一个下标。
1.贪心算法:
先写代码
class Solution {
public boolean canJump(int[] nums) {
int max = 0;
for(int i=0;i<nums.length;i++){
if(i<=max){
max = Math.max(i+nums[i],max);
}
if(max>=(nums.length-1)){
return true;
}
}
return false;
}
}
来举个例子:
[2, 3, 1, 1, 4]
我们一开始在位置 00,可以跳跃的最大长度为 22,因此最远可以到达的位置被更新为 22;
我们遍历到位置 11,由于 1≤2,因此位置 11 可达。我们用 11 加上它可以跳跃的最大长度 33,将最远可以到达的位置更新为 44。由于 44 大于等于最后一个位置 44,因此我们直接返回 True。
这时我们再看看代码
为什么是i<=max,因为只有当i<=max时,i-max的这块区间他都可以取到,而其他的区间却不行
max(i+nums[i],max)是为了确定到底是现在的这个位置远还是原来的位置远
2.动态规划:
2022.10.20(10.21补上这道题)
跳跃游戏 II
给你一个非负整数数组 nums ,你最初位于数组的第一个位置。
数组中的每个元素代表你在该位置可以跳跃的最大长度。
你的目标是使用最少的跳跃次数到达数组的最后一个位置。
假设你总是可以到达数组的最后一个位置。
示例 1:
输入: nums = [2,3,1,1,4]
输出: 2
解释: 跳到最后一个位置的最小跳跃数是 2。
从下标为 0 跳到下标为 1 的位置,跳 1 步,然后跳 3 步到达数组的最后一个位置。
示例 2:输入: nums = [2,3,0,1,4]
输出: 2
1 <= nums.length <= 104
0 <= nums[i] <= 1000
1.动态规划:
class Solution {
public int jump(int[] nums) {
int dp[] = new int[nums.length];
int min = 10000;
int length = 0;
for(int i = 1;i<nums.length;i++){
dp[i] = min;
}
for(int i=1;i<nums.length;i++){
for(int j = 0;j<i;j++){
if(i<=(j+nums[j])){
dp[i]=Math.min(dp[j]+1,dp[i]);
length = i;
}
}
}
return dp[length];
}
}
也还是先举个例子吧
[2, 3, 1, 1, 4]
index:0 1 2 3 4
2 3 1 1 4
dp: 0 ? ? ? ?
反正dp[0]为0没什么争议的,剩下的dp[1]到dp[nums.length-1]把它们设置为大一点的值,可以把它们设置为nums.length也可以把它们设置为10000
index:0 1 2 3 4
2 3 1 1 4
dp: 0 ? ? ? ?
nums[0]可以直接跳到nums[1]和nums[2]
更新:
index:0 1 2 3 4
2 3 1 1 4
dp: 0 1 1 ? ?
到nums[1],它可以跳到nums[2],nums[3],nums[4]
更新:
index:0 1 2 3 4
2 3 1 1 4
dp: 0 1 1 2 2
注意dp[2]仍然是1而不是2,(1<2这是常识)
后面就不用看了,肯定比2大,
所以最小就是2
2.贪心算法:
代码
if(nums.length==1){
return 0;
}
else{
int w = nums.length-1;
int step = 0;
int i = 0;
boolean a = true;
while(a){
if(nums[i]+i<w){
i++;
}
else{
int sum = nums[i]+i;
step++;
w = i;
if(w!=0){
i=0;
}
else{
a = false;
}
}
}
return step;
}
分两种情况,第一种当nums只有一个元素的情况下,直接return 0
第二种情况,nums中的元素大于1,找出最远的能刚好跳出去的位置,把这个位置定义为w,把i的值赋给w,运行的step+1,然后再看在w之前哪个i能最远到达w,最终得到step
2022.10.21
最大子数组和
给你一个整数数组 nums
,请你找出一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。
子数组 是数组中的一个连续部分。
示例 1:
输入:nums = [-2,1,-3,4,-1,2,1,-5,4]
输出:6
解释:连续子数组 [4,-1,2,1] 的和最大,为 6 。
示例 2:
输入:nums = [1]
输出:1
示例 3:
输入:nums = [5,4,-1,7,8]
输出:23
提示:
1 <= nums.length <= 105
-104 <= nums[i] <= 104
1.动态规划:
class Solution {
public int maxSubArray(int[] nums) {
int dp[] = new int[nums.length];;
int max = nums[0];
dp[0]=nums[0];
for(int i=1;i<nums.length;i++){
if(dp[i-1]>0){
dp[i] = dp[i-1]+nums[i];
}
else{
dp[i]=nums[i];
}
}
for(int i = 0; i < nums.length;i++){
max = max>dp[i]?max:dp[i];
}
return max;
}
}
感觉是一种状态转移,感觉看代码就可以理解,但还是举一个例子吧
index:0 1 2 3 4 5 6 7 8
-2,1,-3,4,-1,2,1,-5,4
dp: -2 ? ? ? ? ? ? ? ?
index为1时
index:0 1 2 3 4 5 6 7 8
-2,1,-3,4,-1,2,1,-5,4
dp: -2 1 ? ? ? ? ? ? ?
index 为2
index:0 1 2 3 4 5 6 7 8
-2,1,-3,4,-1,2,1,-5,4
dp: -2 1 -2 ? ? ? ? ? ?
index 为3
index:0 1 2 3 4 5 6 7 8
-2,1,-3,4,-1,2,1,-5,4
dp: -2 1 -2 2 ? ? ? ? ?
index 为4
index:0 1 2 3 4 5 6 7 8
-2,1,-3,4,-1,2,1,-5,4
dp: -2 1 -2 2 1 ? ? ? ?
index 为5
index:0 1 2 3 4 5 6 7 8
-2,1,-3,4,-1,2,1,-5,4
dp: -2 1 -2 2 1 3 ? ? ?
index 为6
index:0 1 2 3 4 5 6 7 8
-2,1,-3,4,-1,2,1,-5,4
dp: -2 1 -2 2 1 3 4 ? ?
index 为7
index:0 1 2 3 4 5 6 7 8
-2,1,-3,4,-1,2,1,-5,4
dp: -2 1 -2 2 1 3 4 -1 ?
index 为8
index:0 1 2 3 4 5 6 7 8
-2, 1,-3, 4, -1,2,1,-5, 4
dp: -2 1 -2 2 1 3 4 -1 4
所以最大就是4