公式为:dpij = dpi-1j+dpij-1
初始化第一行第一列的值为1,任意IJ位置可以由上面和左边加起来得到路径数量
公式为:gridij = Math.min(gridi-1j,gridij-1)+gridij;
原地修改数组。
class Solution {
public int minPathSum(int[][] grid) {
for(int i = 0; i < grid.length; i++){
for(int j = 0; j < grid[0].length; j++){
if(i == 0 && j ==0){
continue;
}
if(i == 0){
grid[0][j] =grid[0][j-1]+grid[0][j];
}else if(j == 0){
grid[i][0] =grid[i-1][0]+grid[i][0];
} else
grid[i][j] = Math.min(grid[i-1][j],grid[i][j-1])+grid[i][j];
}
}
return grid[grid.length-1][grid[0].length-1];
}
}
第一次写错了,错误原因为遍历顺序和初始化错误
class Solution {
public String longestPalindrome(String s) {
// dp[i][j] = dp[i+1][j-1] + i,j是否相等
// i, 0-n j i+1->n
int maxl = 0;
int maxr = 0;
int maxlen = 1;
boolean dp[][] = new boolean[s.length()][s.length()];
for(int i = 0)
for(int i = 0; i < s.length();i++){
for(int j = i + 1; j < s.length();j++){
if(s.charAt(i) == s.charAt(j) && (j-i==1||dp[i+1][j-1])){
dp[i][j]=true;
if(j-i+1 > maxlen){
maxlen = j-i+1;
maxl = i;
maxr = j;
}
}
}
}
return s.substring(maxl, maxr+1);
}
}
公式为:dpij = (s.charAt(i) == s.charAt(j)) && (j - i <= 1 || dpi + 1j - 1);
i,j位置依赖的是左下的状态的值,需要让左下的值优先计算
修正后的代码如下:遍历顺序修改,需要从行最大列最小的开始计算,同时保证J>=I
class Solution {
public String longestPalindrome(String s) {
// dp[i][j] = dp[i+1][j-1] + i,j是否相等
// i, 0-n j i+1->n
int maxl = 0;
int maxr = 0;
int maxlen = 1;
boolean dp[][] = new boolean[s.length()][s.length()];
// 保证j>=i
for(int i = s.length()-1; i >= 0;i--){
for(int j = i; j < s.length();j++){
if(s.charAt(i) == s.charAt(j) && (j-i<=1||dp[i+1][j-1])){
dp[i][j]=true;
if(j-i+1 > maxlen){
maxlen = j-i+1;
maxl = i;
maxr = j;
}
}
}
}
return s.substring(maxl, maxr+1);
}
}
第一次写错:考虑从0,i和从0,j的索引位置的字符串组成的最大长度,初始化写的不对
不如改定义:推理含义: 两个字符串分别从前i和j个字符组成子序列的最大长度
//dpij = dpi-1j-1 +1 if (i==j)
// else = max(dpi-1j, dpij-1)
// 推理顺序:依赖左上的数据,都是从小到大遍历
// 初始化:不需要初始化
注意点为:获取字符的索引位置修改为i-1,j-1,保证dp的索引也不越界,到0行0列都为0.
class Solution {
public int longestCommonSubsequence(String text1, String text2) {
//推理含义: 两个字符串分别从前i和j个字符组成子序列的最大长度
//dp[i][j] = dp[i-1][j-1] +1 if (i==j)
// else = max(dp[i-1][j], dp[i][j-1])
// 依赖左上的数据,都是从小到大遍历
// 初始化:不需要初始化
int dp[][] = new int[text1.length()+1][text2.length()+1];
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()];
}
}
基于最长公共子序列思想
dpij代表把 word1 的前 i 个字符变成 word2 的前 j 个字符,所需的最少操作数。
基于哨兵做边界初始化防止-1越界
定义状态转移方程
if(word1.charAt(i-1) == word2.charAt(j-1)){
dp[i][j] = dp[i-1][j-1];
}else{
int temp = Math.min(dp[i-1][j], dp[i-1][j-1]);
dp[i][j] = Math.min(temp, dp[i][j-1])+1;
}
遍历顺序,依赖左上、上、左 → i、j 都从小到大
class Solution {
public int minDistance(String word1, String word2) {
int m = word1.length();
int n = word2.length();
// 依赖左边上边,左上,可以顺序遍历
int dp[][] = new int[m+1][n+1];
// word2为空的时候,操作第一个字符串需要删除的次数
for(int i = 0; i <=m;i++){
dp[i][0]=i;
}
// word1为空的时候,操作第一个字符串需要插入的次数
for(int j = 0; j <=n;j++){
dp[0][j]=j;
}
for(int i = 1; i <=m;i++){
for(int j = 1; j <=n;j++){
// 如果ij相等,不用操作,考虑前面的就可以
if(word1.charAt(i-1) == word2.charAt(j-1)){
dp[i][j] = dp[i-1][j-1];
}else{
// 否则,考虑i替换为j,或者i后面继续插入j位置的字符,或者删除i位置字符
int temp = Math.min(dp[i-1][j], dp[i-1][j-1]);
dp[i][j] = Math.min(temp, dp[i][j-1])+1;
}
}
}
return dp[m][n];
}
}
总结:
考虑初始化,数组大小,状态方程,遍历顺序。