一)交错字符串:
97. 交错字符串 - 力扣(LeetCode)
一)确定一个状态标识:
如果我选择s1的一段区间,再进行选择s2得一段区间那么s3这个字符串的长度就已经固定了
预处理:在s1字符串s2字符串和s3字符串前面加上一个虚拟字符,让下标从1开始进行计数
如果要是从1号位置开始进行计数的话会变得十分方便
s1=" "+s1
s2=" "+s2
s3=" "+s3
状态表示:dp[i][j]就表示s1中的[1,i]区间内的字符串以及s2[1,j]区间内的字符串能否拼接成s3的[1,i+j]区间内的字符串,如果能够拼接而成,那么里面存的值就是true,否则里面的值就是false;
二)根据状态表示推导状态转移方程:根据最后一个位置的情况来分情况进行讨论
1)如果s1的[0,i]区间和s2的[0,j]区间能够拼接成s3字符串的[0,i+j]区间,那么s3字符串的的i+j位置要么是等于s1的i字符,要么是等于s2的j字符,要么都不等于
2)如果能够拼接而成,要么等于s1的最后一个字符,要么等于s2的最后一个字符
if(s1[i]==s3[i+j])那么此时应该研究的就是s1字符串的[0,i-1]区间内的字符串和s2字符串的[0,j]区间内的字符串是否能够构成s3字符串从[0,i-1+j]的子串
if(s1[i]==s3[i+j]) dp[i][j]=dp[i-1][j]
if(s2[j]==s3[i+j]) dp[i][j]=dp[i][j-1]
三)初始化:
引入空串,在原始的dp表中新增加了一行并且新增加了一列
1)dp[0][0]表示的是第一个字符串是空串况且第二个字符串也是空串,空串拼接空串当然可以
2)现在来看dp[0][i]位置的值(i>0),如果第一个字符串为空,第二个字符串不为空,如果想拼接成第三个字符串,那么必须是对应位置的字符相等才可以,也就是第二个字符串和第三个字符串必须完全相等才可以,如果出现一个位置对应字符不相等,全部白玩
四)填表顺序+返回值:从上向下填写每一行,每一行从左向右进行编写,返回值是dp[m][n]
class Solution { public boolean isInterleave(String s1, String s2, String s3) { if(s1.length()+s2.length()!=s3.length()) return false; //1.先针对初始字符串进行预处理 s1="-"+s1; s2="-"+s2; s3="-"+s3; char[] array1=s1.toCharArray(); char[] array2=s2.toCharArray(); char[] array3=s3.toCharArray(); //2.创建一个dp表,先进行初始化 boolean[][] dp=new boolean[array1.length][array2.length]; dp[0][0]=true; for(int i=1;i
思考一下这么写为什么不对
二)两个字符串删除的最小ASCILL码和
712. 两个字符串的最小ASCII删除和 - 力扣(LeetCode)
我们可以将这个题转化成求两个字符串的公共子序列的ASCILL码和最大的那一个
分析:我们求的是两个字符串的所有公共子序列中,ASCILL码值的最大和,运用正难则反的思想
712. 两个字符串的最小ASCII删除和 - 力扣(LeetCode)
一)定义一个状态表示:
dp[i][j]表示从s1字符串[0,i]区间和s2字符串[0,j]区间的所有给公共子序列中,两个字符串的公共子序列中ASCILL码和最大和
二)根据状态标识推导状态转移方程:根据最后一个位置来划分问题,分情况讨论
要找公共子序列,是从s1里面拉出一个子序列,也需要从s2里面拉出一个子序列,判断他们是否是公共子序列,如果是,求出它们的和,从而找到最大和
if(s1[i]==s2[j]) dp[i][j]=dp[i-1][j-1]+s1[i]的ASCILL码值
if(s1[i]!=s2[j]) dp[i][j]=Math.max(dp[i-1][j] dp[i][j-1],dp[i-1][j-1])
既然要求公共子序列,那么一定是包含一下几种情况:
1)包含s1[i],包含s2[j]:if(s1[i]==s2[j])->dp[i][j]=dp[i-1][j-1]+s1[i]的ASCILL码
2)包含s1[i],不包含s2[j]:dp[i][j]=dp[i][j-1](但是这个状态标识和状态2是完全不对等的,这个状态包含了包含s1[i],不包含s2[j]和不包含s1[i],不包含s2[j]
3)不包含s1[i],包含s2[j]:dp[i][j]=dp[i-1][j]和上面同理,不包含i一定可以保证,但是也是可以不包含s2[j]的
4)不包含s1[i]不包含s2[j] dp[i][j]=dp[i-1][j-1]
上面的2 3情况已经包含了第四种情况
三)初始化+填表顺序+返回值:
1)引入空串:引入一行一列,第一行表示第一个字符串是空的情况,第一列表示第一个字符串是空的情况,当有一个字符串是空的时候,根本就无法找到ASCILL码值最大的子序列的和
2)下标的映射关系:多加一行多加一列之后,下标已经统一向右下角移动了一位,此时下标一定要-1
3)填表顺序:从上到下,每一行从左向右
4)返回值:两个字符串的ASCILL总和-2*dp[m][n]
class Solution { public int minimumDeleteSum(String s1, String s2) { int sum=0; for(int i=0;i
三)最长重复子数组:
718. 最长重复子数组 - 力扣(LeetCode)
一)定义一个状态表示:
1)我们选择第一个数组[0,i]这段区间,我们选择第二个数组[0,j]这段区间,我们想要的是最长重复子数组,我们在第一个区间内找到所有的子数组,我们在第二个区间内找到所有的子数组,我们统计一下两个区间内最长的重复子数组统计一下长度即可
dp[i][j]表示第一个数组在[0,i]区间的所有子数组和[0,j]区间内第二个数组的所有子数组中最长的那一个;
2)选取[0,i]区间内的所有子数组作为研究对象,当我们去进行推导i+1状态的时候,会是使用到前面这些状态的,当我们在进行推导i+1状态的值的时候,想要跟在某一个子数组的后面,就必须跟在i位置的后面,所以我们在进行推导i+1状态的时候,还需要看一下i位置子数组的状态,但是进行研究子序列问题的时候,i+1都是可以跟在前面的所有值得后面的,但是子数组就不行了
3)研究子数组问题:dp[i][j]表示nums1以i位置为结尾的所有的子数组以及nums2以j位置为结尾的所有子数组中,dp[i][j]存的值是最长重复子数组的长度
二)根据状态表示推到状态转移方程:
if(nums[i]==nums2[j]) dp[i][j]=dp[i-1][j-1]+1
else dp[i][j]=0
class Solution { public int findLength(int[] nums1, int[] nums2) { int max=0; int[][] dp=new int[nums1.length+1][nums2.length+1]; 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]=0; } max=Math.max(dp[i][j],max); } } return max; } }
五)最长公共前缀:
一)定义一个状态表示:dp[i]表示以i位置为结尾的字符串数组的最长公共前缀
二)根据状态表示状态转移方程:
1)根据最近的一步来进行划分问题,如果发现这个字符串所有的i位置的元素都是相等的,那么最长公共前缀就是dp[i-1]+"ch",但是如果出现了abcde和hhhhe这种情况,如果发现以i-1位置为结尾的最长公共公共前缀是空,那么dp[i]也是空,虽然相等但是没有最长公共前缀
2)如果发现各个字符串在i位置的元素不是全部都相等,那么直接返回dp[i-1]的值
class Solution { public String longestCommonPrefix(String[] strs) { //dp[i]表示以i位置为结尾字符串数组的最长公共前缀 String minString=strs[0]; for(String str:strs){ if(minString.length()>=str.length()) minString=str; } if(minString.equals("")) return ""; //1.先进行初始化 String[] dp=new String[minString.length()]; char ch=strs[0].charAt(0); dp[0]=ch+""; for(String str:strs){ if(str.charAt(0)!=ch){ dp[0]=""; break; } } //2.进行填表 for(int i=1;i