动规二刷拉拉

这里写自定义目录标题

      • 509.斐波那契数
      • 70.爬楼梯
      • 746.使用最小花费爬楼梯
      • 62.不同路径
      • 63.不同路径||
      • 343.整数拆分
      • 416.分割等和子集
      • 1049.最后一块石头的重量
      • 494.目标和
    • 474.一和零
      • 零钱兑换||
      • 组合总和IV
      • 322.零钱兑换
      • 279.完全平方数
      • 139.单词拆分
      • 198.打家劫舍
      • 213.打家劫舍||
      • 121.买卖股票的最大时机
      • 122.买卖股票的最佳时机||
      • 123.买卖股票的最佳时机|||
      • 188.买卖股票的最佳时机IV
      • 309.最佳买卖股票时机含冷冻期
      • 714.买卖股票的最佳时机含手续费
      • 300.最长递增子序列
      • 674.最长连续递增子序列
      • 718.最长重复子数组
      • 1143.最长公共子序列
      • 1035. 不相交的线
      • 53.最大子数组和
      • 392.判断子序列
      • 115.不同的子序列
      • 583.两个字符串的删除操作
      • 72.编辑距离
      • 647.回文子串
      • 516.最长回文子序列

509.斐波那契数

class Solution {
    public int fib(int n) {
		 if(n==0) {
			 return 0;
		 }
		 if(n==1) {
			 return 1;
		 }
		 
		 int dp[]=new int[n+1];
		 //初始化
		 dp[0]=0;
		 dp[1]=1;
		
		 
		 for(int i=2;i<=n;i++) {
			 dp[i]=dp[i-1]+dp[i-2];
		 }
		 return dp[n];

	  }
}

70.爬楼梯

class Solution {
public int climbStairs(int n) {
		 if(n==1) {
			 return 1;
		 }
		 if(n==2) {
			 return 2;
		 }
		 int []dp=new int[n+1];
		 //初始化
		 dp[1]=1;
		 dp[2]=2;
		 for(int i=3;i<=n;i++) {
			 dp[i]=dp[i-1]+dp[i-2];
		 }
		 return dp[n];

	    }
}

746.使用最小花费爬楼梯

 public int minCostClimbingStairs(int[] cost) {
		 //定义dp数组含义
		 int dp[]=new int[cost.length+1];
		 //初始化
		 dp[0]=0;
		 dp[1]=0;
		 for(int i=2;i<=cost.length;i++) {
			 dp[i]=Math.min(dp[i-1]+cost[i-1], dp[i-2]+cost[i-2]);
		 }
		 System.out.println(Arrays.toString(dp));
		 return dp[cost.length];

	    }

62.不同路径

 public int uniquePaths(int m, int n) {
		  int dp[][]=new int[m][n];//到i,j这个位置有dp[i-1][j-1]跳路径
		  //初始化
		  
		  for(int i=0;i<m;i++) {
			  dp[i][0]=1;
		  }
		  for(int j=0;j<n;j++) {
			  dp[0][j]=1;
		  }
		  //递推公式
		  for(int i=1;i<m;i++) {
			  for(int j=1;j<n;j++) {
				  //不是从上边走过来就是从右边走过来
				  dp[i][j]=dp[i-1][j]+dp[i][j-1];
			  }
		  }
		  
		  return dp[m-1][n-1];

	    }

63.不同路径||

for里边的循环条件,如果到某一部终止的话,是会直接终止的,不会继续走到下一步。

  public int uniquePathsWithObstacles(int[][] obstacleGrid) {
		  //定义dp数组
		  int m=obstacleGrid.length;
		  int n=obstacleGrid[0].length;
		  int dp[][]=new int[m][n];
		  //初始化,当遇到障碍物时,后边的直接不初始化了
		  for(int i=0;i<m&&obstacleGrid[i][0]==0;i++) {
			  dp[i][0]=1;
		  }
		  for(int j=0;j<n&&obstacleGrid[0][j]==0;j++) {
			  dp[0][j]=1;
		  }
		  //递推公式
		  for(int i=1;i<m;i++) {
			  for(int j=1;j<n;j++) {
				  if(obstacleGrid[i][j]==0) {
					  dp[i][j]=dp[i-1][j]+dp[i][j-1];
				  }
			  }
		  }
		  return dp[m-1][n-1];

	    }

343.整数拆分

class Solution {
    
	  public int integerBreak(int n) {
		  int dp[]=new int[n+1];//定义dp数组就是第i个能拆分出的最大乘积数
		  //初始化
		  dp[0]=0;
		  dp[1]=1;
		  dp[2]=1;
		 
		  for(int i=3;i<=n;i++) {
			 for(int j=1;j<=i-j;j++) {
				 dp[i]=Math.max(dp[i], Math.max(j*(i-j), j*dp[i-j]));
				 
			 }
		  }
          System.out.println(Arrays.toString(dp));
		  return dp[n];
	      
	    }


}

416.分割等和子集

//二维背包问题是用上边的和左上角的推出来的,然后层层的去更新数值。
//一维背包是因为要用到左上角的值,所以不能给它覆盖了,所以一位数组要从后往前遍历。要不然就用到本行的数据了。

class Solution {
    public boolean canPartition(int[] nums) {
		  int sum=0;//记录数组中元素的总和
		  for(int i:nums) {
			  sum+=i;
		  }
		  if(sum%2!=0) {//如果背包元素的和是奇数的话,根本不能分成两个
			  return false;
		  }
		  int dp[]=new int[sum/2+1];//分配一个这样大小的背包,如果能正好把物品装满,那恰好能使物品分成两部分
		  for(int i=0;i<nums.length;i++) {//先遍历物品
			  for(int j=sum/2;j>=nums[i];j--) {//再遍历背包
				  dp[j]=Math.max(dp[j], dp[j-nums[i]]+nums[i]);
				  
			  }
			  
		  }
		  return dp[sum/2]==sum/2;

	    }
}

1049.最后一块石头的重量

 public int lastStoneWeightII(int[] stones) {
		  //先求出石块的总和,然后用背包尽可能的装一半,然后用剩下的减去背包里的就是最小的重量了
		  int sum=0;
		  for(int i:stones) {
			  sum+=i;
		  }
		  int target=sum/2;
		  int []dp=new int[target+1];
		  for(int i=0;i<stones.length;i++) {//先遍历物品
			  for(int j=target;j>=stones[i];j--) {
				  dp[j]=Math.max(dp[j], dp[j-stones[i]]+stones[i]);
			  }
			  
		  }
		  return sum-dp[target]-dp[target];

	    }

494.目标和

//有dp[1]了,还需要dp[4]种方法去填满这个背包,
//dp[5]=dp[1]+dp[2]+dp[3]+dp[4]
,商品需要去找存在的东西。

 public int findTargetSumWays(int[] nums, int target) {
         //先求出左边的和的公式,target=(sum+target)/2,然后再求有多少种方法能装满这个背包
		  int sum=0;
		  for(int i:nums) {
			  sum+=i;
		  }
		  
		  int temp=(sum+target)/2;
		  int []dp=new int[temp+1];
		  for(int i=0;i<nums.length;i++) {//先遍历物品
			  for(int j=temp;j>=dp[i];j--) {
				  dp[j]+=dp[j-temp];
			  }
			  
		  }
		  System.out.println(Arrays.toString(dp));
		  return dp[temp];
	    
	  
	  }

474.一和零

用两个维度去装背包,先遍历物品再遍历背包,问装满这个背包最多有多少个物品。

class Solution {
    public int findMaxForm(String[] strs, int m, int n) {
		  int [][]dp=new int[m+1][n+1];//该背包最多有m+1个容量的0和n+1个容量的1,问要装满他最多有多少个子集的长度
		  for(String str:strs) {
			  int zeronums=0;
			  int onenums=0;
			  //算出每个物品的含量
			  for(char c:str.toCharArray()) {
				  if(c=='0') {
					  zeronums++;
				  }else if(c=='1') {
					  onenums++;
				  }
			  }
			  for(int i=m;i>=zeronums;i-- ) {
				  for(int j=n;j>=onenums;j--) {
					  dp[i][j]=Math.max(dp[i-zeronums][j-onenums]+1, dp[i][j]);
				  }
			  }
		  }
		  return dp[m][n];
		  

	    }

}

零钱兑换||

完全背包问题:遍历背包时要从小到大去遍历。
有对应的物品的话,组合次数需要去累加。

class Solution {
  public int change(int amount, int[] coins) {
		  int []dp=new int[amount+1];//有一个容量这么大的背包
		  dp[0]=1;//当背包容量是0的话会有一种情况,就是什么也不装
		  for(int i=0;i<coins.length;i++) {//先遍历物品
			  for(int j=coins[i];j<=amount;j++) {//再遍历背包,完全背包是从小到大去遍历
				  dp[j]+=dp[j-coins[i]];
				  
			  }
			  
		  }
		  
		  return dp[amount];

	    }
}

组合总和IV

要是求排列的话,先遍历背包,再遍历物品,求出来的就是排列数。

  public int change(int amount, int[] coins) {
        //递推表达式
        int[] dp = new int[amount + 1];
        //初始化dp数组,表示金额为0时只有一种情况,也就是什么都不装
        dp[0] = 1;
        for (int i = 0; i < coins.length; i++) {
            for (int j = coins[i]; j <= amount; j++) {//从小到大去遍历时完全背包的问题
                dp[j] += dp[j - coins[i]];
            }
        }
        return dp[amount];
    }

322.零钱兑换

依次去迭代去找最小的一个。dp[j-coins[i]]+1,和dp[j]中取出最小的一个。

class Solution {
     public int coinChange(int[] coins, int amount) {
		  int []dp=new int[amount+1];
		  //初始化
		  dp[0]=0;
		  for(int i=1;i<=amount;i++) {
			  dp[i]=Integer.MAX_VALUE;
		  }
		  //先遍历物品
		  for(int i=0;i<coins.length;i++) {
			  //再遍历背包
			  for(int j=coins[i];j<=amount;j++) {
				  if(dp[j-coins[i]]!=Integer.MAX_VALUE) {
					  dp[j]=Math.min(dp[j], dp[j-coins[i]]+1);
				  }
			  }
		  }
		  return dp[amount]==Integer.MAX_VALUE?-1:dp[amount];

	    }
	  
}

279.完全平方数

class Solution {
    public int numSquares(int n) {
		  //定义dp数组
		  int []dp=new int[n+1];
		  //初始化
		  for(int i=0;i<=n;i++) {
			  dp[i]=Integer.MAX_VALUE;
		  }

          dp[0]=0;
		  //遍历顺序
		  for(int i=1;i*i<=n;i++) {//先遍历物品
			  for(int j=i*i;j<=n;j++) {//再遍历背包
				  if(dp[j-i*i]!=Integer.MAX_VALUE) {
					  dp[j]=Math.min(dp[j], dp[j-i*i]+1);
				  }
				  
			  }
			  
		  }
		  
		  return dp[n];

	    }
}

139.单词拆分

  public boolean wordBreak(String s, List<String> wordDict) {
		  Set<String>set=new HashSet<String>(wordDict);
		  //定义背包
		  boolean[]dp=new boolean[s.length()+1];//判断当前长度的字符串能不能由字典组成
		  //初始化
		  dp[0]=true;
		  
		  for(int i=1;i<=s.length();i++) {//先遍历背包
			  for(int j=0;j<i&&!dp[i];j++) {//再遍历物品
				  if(set.contains(s.substring(j,i))&&dp[j]) {//代表这个单词之前的能匹配上并且这个单词也能匹配上
					  dp[i]=true;
					  
				  }
			  }
			  
		  }
		  
		  return dp[s.length()];
		  

	    }
	  

198.打家劫舍

动态规划的思想去求,dp[i]=Math.max(dp[i-2]+nums[i],dp[i-1]);

class Solution {
   public int rob(int[] nums) {
		  int []dp=new int[nums.length];//定义dp数组,dp[i]表示偷到dp[i]所能偷到钱的最大数量
		  if(nums.length==1) {
			  return nums[0];
		  }
		  dp[0]=nums[0];
		  dp[1]=Math.max(nums[0],nums[1]);
		  for(int i=2;i<nums.length;i++) {
			  dp[i]=Math.max(dp[i-1], dp[i-2]+nums[i]);
		  }
		  return dp[nums.length-1];

	    }
}

213.打家劫舍||

class Solution {
      public int rob(int[] nums) {
		    if(nums.length==1) {
		    	return nums[0];
		    }
		    
		    return Math.max(robhelp(nums,0,nums.length-2), robhelp(nums,1,nums.length-1));

	    }
	  
	  public int robhelp(int[] nums,int start,int end ) {
		   if(start==end) {
			  return nums[start];
		  }
		  int []dp=new int[nums.length];
		  dp[start]=nums[start];
		  dp[start+1]=Math.max(nums[start], nums[start+1]);
		  for(int i=start+2;i<=end;i++) {
			  dp[i]=Math.max(dp[i-2]+nums[i], dp[i-1]);
		  }
		  return dp[end];
		  
	  }
}

思路:解耦成两个函数,然后分别去找。

121.买卖股票的最大时机

思路:
定义好dp数组的含义,然后用动规去找从哪天买入代价最小,然后再找从哪天卖出利益最大。

class Solution {
    public int maxProfit(int[] prices) {
		  
		  int dp[][]=new int[prices.length][2];//dp[i][0]表示持有股票的最大钱数,dp[i][1]表示不持有股票的最大钱数
		  //初始化
		  dp[0][0]=-prices[0];
		  dp[0][1]=0;//不持有的金钱就是0
		  //递推公式
		  for(int i=1;i<prices.length;i++) {
			  dp[i][0]=Math.max(dp[i-1][0],-prices[i]);//决定在哪天买入
			  dp[i][1]=Math.max(dp[i-1][1], dp[i-1][0]+prices[i]);//决定在哪天卖出
		  }
		  return dp[prices.length-1][1];

	    }
}

122.买卖股票的最佳时机||

和第一次的区别就是这次可以买卖好多次了,买股票时的余额就不是0了,而是之前不持有股票的利益。

class Solution {
      public int maxProfit(int[] prices) {
		  
		  int dp[][]=new int[prices.length][2];//dp[i][0]表示持有股票的最大钱数,dp[i][1]表示不持有股票的最大钱数
		  //初始化
		  dp[0][0]=-prices[0];
		  dp[0][1]=0;//不持有的金钱就是0
		  //递推公式
		  for(int i=1;i<prices.length;i++) {
			  dp[i][0]=Math.max(dp[i-1][0],dp[i-1][1]-prices[i]);//决定在哪天买入
			  dp[i][1]=Math.max(dp[i-1][1], dp[i-1][0]+prices[i]);//决定在哪天卖出
		  }
		  return dp[prices.length-1][1];

	    }
}

123.买卖股票的最佳时机|||

只需要定义好四个状态,然后每个状态去相应的模拟,每个状态都是由上一个状态推出来的,去模拟就好了。

class Solution {
    public int maxProfit(int[] prices) {
    	//dp数组的含义
    	//dp[i][0] 不做操作的状态
    	//dp[i][1]第一次持有的状态
    	//dp[i][2]第一次不持有的状态
    	//dp[i][3]第二次持有的状态
    	//dp[i][4]第二次不持有的状态
    	//初始化
    	int[][]dp=new int[prices.length][5];
    	dp[0][0]=0;
    	dp[0][1]=-prices[0];
    	dp[0][2]=0;
    	dp[0][3]=-prices[0];
    	dp[0][4]=0;
    	//遍历顺序
    	for(int i=1;i<prices.length;i++) {
    		dp[i][1]=Math.max(dp[i-1][1], -prices[i]);
    		dp[i][2]=Math.max(dp[i-1][2], dp[i][1]+prices[i]);
    		dp[i][3]=Math.max(dp[i-1][3], dp[i][2]-prices[i]);
    		dp[i][4]=Math.max(dp[i-1][4], dp[i][3]+prices[i]);
    	}
    	return dp[prices.length-1][4];
      
    }
}

188.买卖股票的最佳时机IV

也是奇数状态进行初始化为负数,然后遍历出各个物品的每个状态。

class Solution {
  public int maxProfit(int k, int[] prices) {
		  if (prices.length == 0) return 0;
		  int [][]dp =new int[prices.length][2*k+1];//定义dp数组状态是2k+1个状态
		  
		  //初始化,当第奇数状态的时候是不持有的状态
		  for(int i=1;i<k*2;i+=2) {
			  dp[0][i]=-prices[0];
		  }
		  
		  //递推公式,从第一天开始递推,然后有k个状态都要表示出来
		  for(int i=1;i<prices.length;i++) {
			  for(int j=0;j<2*k-1;j+=2) {
				  dp[i][j+1]=Math.max(dp[i-1][j+1], dp[i-1][j]-prices[i]);
				  dp[i][j+2]=Math.max(dp[i-1][j+2], dp[i-1][j+1]+prices[i]);
			  }
		  }
		  return dp[prices.length-1][2*k];
	        
	    }
}

309.最佳买卖股票时机含冷冻期

思路:将每个状态都列举出来,然后每次去递推,看看哪个状态可以由前边一天的状态推出来。

public int maxProfit(int[] prices) {
		  int [][]dp=new int[prices.length][4];
		  /**
		   * dp数组含义
		   * dp[i][0]持有股票的状态
		   * dp[i][1]保持卖出股票的状态,在冷冻期之后,每天都是保持卖出股票的状态
		   * dp[i][2]当天卖出股票的状态
		   * dp[i][3]冷冻期
		   */
		  //初始化
		  dp[0][0]=-prices[0];
		  dp[0][1]=0;
		  //递推公式
		  for(int i=1;i<prices.length;i++) {
			  dp[i][0]=Math.max(dp[i-1][0], Math.max(dp[i-1][3]-prices[i], dp[i-1][1]-prices[i]));
			  dp[i][1]=Math.max(dp[i-1][1], dp[i-1][3]);//冷冻期的下一天也是保持卖出股票的状态
			  dp[i][2]=dp[i-1][0]+prices[i];
			  dp[i][3]=dp[i-1][2];
			  
		  }
		  
		  return Math.max(dp[prices.length-1][1], Math.max(dp[prices.length-1][2], dp[prices.length-1][3]));

	    }

714.买卖股票的最佳时机含手续费

//只需要在卖出股票的时候把手续费减去就可以了

class Solution {
   public int maxProfit(int[] prices, int fee) {
		  //定义dp数组
		  int dp[][]=new int[prices.length][2];
		  //dp[i][0]持有股票的最大利润
		  //dp[i][1]不持有股票的最大利润
		  //初始化
		  dp[0][0]=-prices[0];
		  dp[0][1]=0;
		  for(int i=1;i<prices.length;i++) {
			  dp[i][0]=Math.max(dp[i-1][0], dp[i-1][1]-prices[i]);
			  dp[i][1]=Math.max(dp[i-1][1], dp[i-1][0]+prices[i]-fee);
		  }
		  return dp[prices.length-1][1];

	    }
}

300.最长递增子序列

从i开始,每次遍历一遍找到比i小的前边的最长递增子序列,然后dp[j]+1即可。

class Solution {
      public int lengthOfLIS(int[] nums) {
		  int []dp=new int[nums.length];//dp[i]表示以i为结尾的字母最长递增子序列的长度是多少
		  dp[0]=1;
		  Arrays.fill(dp, 1);
		  for(int i=1;i<nums.length;i++) {//外边是一套循环,然后里边是第二层循环
			  for(int j=0;j<i;j++) {//再遍历一次找到i前边的字符串
				  if(nums[i]>nums[j]) {
					  dp[i]=Math.max(dp[j]+1, dp[i]);
				  }
			  }
		  }
		  //然后在里边找到这些数组的最大值
		  int result=0;
		  for(int i:dp) {
			  if(i>result) {
				  result=i;
			  }
		  }
		  System.out.println(Arrays.toString(dp));
		  return result;

	    }
}

674.最长连续递增子序列

    public int findLengthOfLCIS(int[] nums) {
		  int []dp=new int[nums.length];//定义dp数组,dp[i]以i为结尾的数组最长连续递增子序列的长度
		  //初始化
		  dp[0]=1;
		  for(int i=1;i<nums.length;i++) {
			  if(nums[i]>nums[i-1]) {
				  dp[i]=dp[i-1]+1;
			  }else {
				  dp[i]=1;//从头开始计数
			  }
		  }
		  //找到dp数组里的最大值
		  int result=0;
		  for(int i:dp) {
			  if(i>result) {
				  result=i;
			  }
		  }
          System.out.println(Arrays.toString(dp));
		  return result;

	    }

718.最长重复子数组

//定义好dp数组,然后挨个去遍历,找到相同的字母要在这个基础上+1

public int findLength(int[] nums1, int[] nums2) {
		  //定义dp数组 ,dp[i][j]表示nums1以i-1为结尾的字符串和nums2以j-1为结尾的字符串最长重复子数组的个数
		  int [][]dp=new int[nums1.length+1][nums2.length+1];
          int result=0;
          //初始化
          dp[0][0]=0;
          for(int i=1;i<=nums1.length;i++) {
        	  for(int j=1;j<=nums2.length;j++) {
        		  if(nums1[i-1]==nums2[j-1]) {
        			  dp[i][j]=dp[i-1][j-1]+1;//代表遇到两个字母相同的话,这个最长重复子数组的长度在之前的基础上+1
        			  
        			  
        		  }
        		  result=Math.max(result, dp[i][j]);
        	  }
          }
          return result;
	    }

1143.最长公共子序列

只需要考虑两个字母不相等的情况即可。

class Solution {
    public int longestCommonSubsequence(String text1, String text2) {
		  int [][]dp=new int[text1.length()+1][text2.length()+1];//dp[i][j]表示text1以i-1为结尾,text2以i-2为结尾的最长重复子数组的长度
		  dp[0][0]=0;
		  for(int i=1;i<=text1.length();i++) {
			  for(int j=1;j<=text2.length();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[text1.length()][text2.length()];//因为每次都更新一个值,所以不用更新到最后

	    }
}

1035. 不相交的线

还是求最长公共子序列

class Solution {
      public int maxUncrossedLines(int[] nums1, int[] nums2) {
		  int [][] dp=new int[nums1.length+1][nums2.length+1];//定义dp数组,dp[i][j]表示以nums1以i-1为结尾,text2以i-2为结尾的最长重复子数组的长度
		  dp[0][0]=0;
		  for(int i=1;i<=nums1.length;i++) {
			  for(int j=1;j<=nums2.length;j++) {
				  if(nums1[i-1]==nums2[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[nums1.length][nums2.length];
		

	    }
}

53.最大子数组和

//要考虑 result也和dp[0]比较一下。

class Solution {
   public int maxSubArray(int[] nums) {
		  int []dp=new int[nums.length];//dp[i]表示以下标i结尾的最大子序和的数组
		  //初始化
		  if(nums.length==1) {
			  return nums[0];
		  }
		  dp[0]=nums[0];
		  
		  int result=Integer.MIN_VALUE;//存储最大值
		  result=Math.max(result, dp[0]);
		  for(int i=1;i<nums.length;i++) {
			  dp[i]=Math.max(dp[i-1]+nums[i], nums[i]);
			  result=Math.max(result, dp[i]);
		  }
		  return result;
		  

	    }

}

392.判断子序列

 public boolean isSubsequence(String s, String t) {
		  int [][]dp=new int[s.length()+1][t.length()+1];//定义dp[][]数组求出两个字符串的最长公共子序列,然后判断这两个序列是否相等
		  dp[0][0]=0;
		  for(int i=1;i<=s.length();i++) {
			  for(int j=1;j<=t.length();j++) {
				  if(s.charAt(i-1)==t.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[s.length()][t.length()]==s.length();

	    }

115.不同的子序列

 public int numDistinct(String s, String t) {
		  int dp[][]=new int[s.length()+1][t.length()+1];//dp[i][j]表示s以i-1为结尾的序列中有几个t以j-1结尾的子序列
		  //初始化
		  dp[0][0]=1;
		  dp[0][1]=0;
		  dp[1][0]=1;//前边有一个字符的情况肯定包含第二个字符串没有字符的情况。
		  for (int i = 0; i < s.length() + 1; i++) {
	            dp[i][0] = 1;
	        }
		  for(int i=1;i<=s.length();i++) {
			  for(int j=1;j<=t.length();j++) {
				  if(s.charAt(i-1)==t.charAt(j-1)) {//当字符串里下标为i-1和j-1的字符相等时,就考虑它们前一个字母的情况了
					  dp[i][j]=dp[i-1][j-1]+dp[i-1][j];//即不考虑这个字母的情况和不考虑s中的i-1的字母的情况
					  
				  }else {
					  dp[i][j]=dp[i-1][j];//当两个字母不相等的时候就等于前一个字母的情况。
				  }
			  }
		  }
		  return dp[s.length()][t.length()];

	    }

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

class Solution {
public int minDistance(String word1, String word2) {
int dp[][]=new int[word1.length()+1][word2.length()+1];//定义dp数组dp[i][j]表示word1以i-1结尾的和word2以j-1结尾的字符串要达到相同的长度最少删除几个字符串
//初始化
for(int i=0;i<=word1.length();i++) {
dp[i][0]=i;
}
for(int j=0;j<=word2.length();j++) {
dp[0][j]=j;
}
//递推公式
for(int i=1;i<=word1.length();i++) {
for(int j=1;j<=word2.length();j++) {
if(word1.charAt(i-1)==word2.charAt(j-1)) {//如果两个字母相同的话,dp数组就等于它们的上一个字母
dp[i][j]=dp[i-1][j-1];

			  }else {//如果两个字母不相同的话,就在删第一个字符串的一个字母,删第二个字符串的一个字母,两个字符串的字母都删了,这三种情况中选取一个
				  dp[i][j]=Math.min(dp[i-1][j]+1, Math.min(dp[i][j-1]+1, dp[i-1][j-1]+2));
				  
			  }
		  }
	  }
	  return dp[word1.length()][word2.length()];

    }

}

//如果两个字母相同的话,就找它对应的前一个,如果不相同的话,就在删上一个字母、删下一个字母、两个字母都删了这两种情况中选一个。

72.编辑距离

class Solution {
  public int minDistance(String word1, String word2) {
		  int dp[][]=new int[word1.length()+1][word2.length()+1];//定义dp数组dp[i][j]表示word1以i-1结尾的和word2以j-1结尾的字符串要达到相同的长度最少删除几个字符串
		  //初始化
		  for(int i=0;i<=word1.length();i++) {
			  dp[i][0]=i;
		  }
		  for(int j=0;j<=word2.length();j++) {
			  dp[0][j]=j;
		  }
		  //递推公式
		  for(int i=1;i<=word1.length();i++) {
			  for(int j=1;j<=word2.length();j++) {
				  if(word1.charAt(i-1)==word2.charAt(j-1)) {//如果两个字母相同的话,dp数组就等于它们的上一个字母
					  dp[i][j]=dp[i-1][j-1];
					  
				  }else {//如果两个字母不相同的话,就在删第一个字符串的一个字母,删第二个字符串的一个字母,两个字符串的字母都删了,这三种情况中选取一个
					  dp[i][j]=Math.min(dp[i-1][j]+1, Math.min(dp[i][j-1]+1, dp[i-1][j-1]+1));//第三种情况就是一个替换的操作,所以就+1,而不是加2了
					  
				  }
			  }
		  }
		  return dp[word1.length()][word2.length()];

	    }

}

647.回文子串

class Solution {
    public int countSubstrings(String s) {
		  
		  boolean [][]dp=new boolean [s.length()][s.length()];//定义dp数组,dp[i][j]表示以[i,j]为下标的字符串是不是回文子串
		  //初始化,要初始化为false。
		  //遍历顺序,因为dp[i][j]要从dp[i+1][j-1]推出来,所以要从左下角到右上角遍历
		  int result=0;//记录回文字符串的总数
		  
		  for(int i=s.length()-1;i>=0;i--) {
			  for(int j=i;j<s.length();j++) {
				  if(s.charAt(i)==s.charAt(j)) {//当遇到的两个指针相等了
					  if(j-i<=1) {//这个肯定是回文字符串
						  dp[i][j]=true;
						  result++;
					  }else if(dp[i+1][j-1]){//如果它里边的是一个回文串,那么当两个字母相等的时候它才是回文串
						  dp[i][j]=true;
						  result++;
						  
					  }
				  }//两个字母不相等的时候默认为false
			  }
		  }
		  return result;

	    }
}

516.最长回文子序列

//p判断两个字母相等的时候+2,不相等的时候减去其中一个字母就行了。

  public int longestPalindromeSubseq(String s) {
		  int dp[][]=new int [s.length()][s.length()];//dp[i][j]表示以i为开头,以j为结尾的字符串最长回文子串
		  //初始化
		  for(int i=0;i<s.length();i++) {
			  dp[i][i]=1;
		  }
		  //遍历顺序,从左下角遍历到右上角
		  for(int i=s.length()-1;i>=0;i--) {
			  for(int j=i+1;j<s.length();j++) {
				  if(s.charAt(i)==s.charAt(j)) {//如果两个字符相等的话,证明这就是一个回文子串,然后在这个回文子串的基础上+2
					  dp[i][j]=dp[i+1][j-1]+2;
				  }else {//不是回文子串,然后在去掉首尾字符的两种情况下-1
					  dp[i][j]=Math.max(dp[i+1][j], dp[i][j-1]);
				  }
			  }
			  
		  }
		  return dp[0][s.length()-1];

	    }

你可能感兴趣的:(算法)