本博客文章(学习笔记)导航 (点击这里访问)
BM62 斐波那契数列
思路:
递归
非递归
public class Solution {
public int Fibonacci ( int n) {
if ( n<= 2 ) return 1 ;
return Fibonacci ( n- 1 ) + Fibonacci ( n- 2 ) ;
}
}
public class Solution {
public int Fibonacci ( int n) {
if ( n<= 2 ) return 1 ;
int a= 1 , b= 1 , sum= a+ b;
for ( int i= 3 ; i<= n; i++ ) {
sum= a+ b;
a= b;
b= sum;
}
return sum;
}
}
BM63 跳台阶
描述
一只青蛙一次可以跳上1级台阶,也可以跳上2级。求该青蛙跳上一个 n 级的台阶总共有多少种跳法(先后次序不同算不同的结果)。
数据范围:1≤n≤40
要求:时间复杂度:O(n) ,空间复杂度: O(1)
示例1
输入:2
返回值:2
说明:青蛙要跳上两级台阶有两种跳法,分别是:先跳一级,再跳一级或者直接跳两级。因此答案为2
示例2
输入:7
返回值:21
public class Solution {
public int jumpFloor ( int target) {
if ( target<= 1 ) return 1 ;
int a= 1 , b= 1 , sum= a+ b;
for ( int i= 2 ; i<= target; i++ ) {
sum= a+ b;
a= b;
b= sum;
}
return sum;
}
}
BM64 最小花费爬楼梯
描述
给定一个整数数组 cost ,其中 cost[ i] 是从楼梯第i个台阶向上爬需要支付的费用,下标从0 开始。一旦你支付此费用,即可选择向上爬一个或者两个台阶。
你可以选择从下标为 0 或下标为 1 的台阶开始爬楼梯。
请你计算并返回达到楼梯顶部的最低花费。
数据范围:数组长度满足 1 ≤n≤10 ^ 5 ,数组中的值满足 1 ≤cost≤10 ^ 4
示例1
输入:[ 2 , 5 , 20 ]
返回值:5
说明:你将从下标为1 的台阶开始,支付5 ,向上爬两个台阶,到达楼梯顶部。总花费为5
示例2
输入:[ 1 , 100 , 1 , 1 , 1 , 90 , 1 , 1 , 80 , 1 ]
返回值:6
说明:
你将从下标为 0 的台阶开始。
1. 支付 1 ,向上爬两个台阶,到达下标为 2 的台阶。
2. 支付 1 ,向上爬两个台阶,到达下标为 4 的台阶。
3. 支付 1 ,向上爬两个台阶,到达下标为 6 的台阶。
4. 支付 1 ,向上爬一个台阶,到达下标为 7 的台阶。
5. 支付 1 ,向上爬两个台阶,到达下标为 9 的台阶。
6. 支付 1 ,向上爬一个台阶,到达楼梯顶部。
总花费为 6 。
import java. util. ;
public class Solution {
public int minCostClimbingStairs ( int [ ] cost) {
int n= cost. length;
if ( n== 1 || n== 2 ) return 0 ;
int [ ] dp= new int [ n+ 1 ] ;
dp[ 0 ] = 0 ;
dp[ 1 ] = 0 ;
for ( int i= 2 ; i<= n; i++ ) {
dp[ i] = Math . min ( dp[ i- 1 ] + cost[ i- 1 ] , dp[ i- 2 ] + cost[ i- 2 ] ) ;
}
return dp[ n] ;
}
}
BM65 最长公共子序列(二)
描述
给定两个字符串str1和str2,输出两个字符串的最长公共子序列。如果最长公共子序列为空,则返回"-1"。目前给出的数据,仅仅会存在一个最长的公共子序列
数据范围:0≤∣str1∣,∣str2∣≤2000
要求:空间复杂度 O(n^2),时间复杂度 O(n^2)
示例1
输入:"1A2C3D4B56","B1D23A456A"
返回值:"123456"
示例2
输入:"abc","def"
返回值:"-1"
示例3
输入:"abc","abc"
返回值:"abc"
示例4
输入:"ab",""
返回值:"-1"
import java. util. ;
public class Solution {
public String LCS ( String s1, String s2) {
int n= s1. length ( ) ;
int m= s2. length ( ) ;
String [ ] [ ] dp= new String [ n+ 1 ] [ m+ 1 ] ;
for ( int i= 0 ; i<= n; i++ ) {
for ( int j= 0 ; j<= m; j++ ) {
if ( i== 0 || j== 0 ) {
dp[ i] [ j] = "" ;
} else if ( s1. charAt ( i- 1 ) == s2. charAt ( j- 1 ) ) {
dp[ i] [ j] = dp[ i- 1 ] [ j- 1 ] + s1. charAt ( i- 1 ) ;
} else {
dp[ i] [ j] = dp[ i- 1 ] [ j] . length ( ) > dp[ i] [ j- 1 ] . length ( ) ? dp[ i- 1 ] [ j] : dp[ i] [ j- 1 ] ;
}
}
}
return dp[ n] [ m] == "" ? "-1" : dp[ n] [ m] ;
}
}
BM66 最长公共子串
描述
给定两个字符串str1和str2,输出两个字符串的最长公共子串题目保证str1和str2的最长公共子串存在且唯一。
数据范围:1≤∣str1∣,∣str2∣≤5000
要求: 空间复杂度 O(n^2),时间复杂度 O(n^2)
示例1
输入:"1AB2345CD","12345EF"
返回值:"2345"
备注:1≤∣str1∣,∣str2∣≤5000
思路:
动态规划
某位置上相等: dp[i + 1][j + 1] = dp[i][j] + 1;
某位置上不等: dp[i + 1][j+1] = 0;
public String LCS ( String str1, String str2) {
int maxLenth = 0 ;
int maxLastIndex = 0 ;
int [ ] [ ] dp = new int [ str1. length ( ) + 1 ] [ str2. length ( ) + 1 ] ;
for ( int i = 0 ; i < str1. length ( ) ; i++ ) {
for ( int j = 0 ; j < str2. length ( ) ; j++ ) {
if ( str1. charAt ( i) == str2. charAt ( j) ) {
dp[ i + 1 ] [ j + 1 ] = dp[ i] [ j] + 1 ;
if ( dp[ i + 1 ] [ j + 1 ] > maxLenth) {
maxLenth = dp[ i + 1 ] [ j+ 1 ] ;
maxLastIndex = i;
}
} else {
dp[ i + 1 ] [ j+ 1 ] = 0 ;
}
}
}
return str1. substring ( maxLastIndex - maxLenth + 1 , maxLastIndex + 1 ) ;
}
BM67 不同路径的数目(一)
描述
一个机器人在m×n大小的地图的左上角(起点)。机器人每次可以向下或向右移动。机器人要到达地图的右下角(终点)。可以有多少种不同的路径从起点走到终点?
备注:m和n小于等于100,并保证计算结果在int范围内 数据范围:0<n,m≤100,保证计算结果在32位整型范围内
要求:空间复杂度O(nm),时间复杂度O(nm)
进阶:空间复杂度O(1),时间复杂度O(min(n,m))
示例1
输入:2,1
返回值:1
示例2
输入:2,2
返回值:2
思路:
动态规划:dp[i][j]=dp[i-1][j]+dp[i][j-1];
每个位置只会从左边或者上边过来
import java. util. ;
public class Solution {
public int uniquePaths ( int m, int n) {
int [ ] [ ] dp= new int [ m+ 1 ] [ n+ 1 ] ;
for ( int i= 1 ; i<= m; i++ ) {
for ( int j= 1 ; j<= n; j++ ) {
if ( i== 1 || j== 1 ) {
dp[ i] [ j] = 1 ;
} else {
dp[ i] [ j] = dp[ i- 1 ] [ j] + dp[ i] [ j- 1 ] ;
}
}
}
return dp[ m] [ n] ;
}
}
BM68 矩阵的最小路径和
描述
给定一个 n m 的矩阵 a,从左上角开始每次只能向右或者向下走,最后到达右下角的位置,路径上所有的数字累加起来就是路径和,输出所有的路径中最小的路径和。
数据范围: 1 ≤n, m≤500 ,矩阵中任意值都满足0 ≤ai, j≤100
要求:时间复杂度 O ( nm)
例如:当输入[ [ 1 , 3 , 5 , 9 ] , [ 8 , 1 , 3 , 4 ] , [ 5 , 0 , 6 , 1 ] , [ 8 , 8 , 4 , 0 ] ] 时,对应的返回值为12 ,
所选择的最小累加和路径如下图所示:
示例1
输入:[[1,3,5,9],[8,1,3,4],[5,0,6,1],[8,8,4,0]]
返回值:12
示例2
输入:[[1,2,3],[1,2,3]]
返回值:7
思路:
动态规划
1 上边只能从左边过来
2 左边只能从上边过来
3 其他的可以从上左两个方向过来
import java. util. ;
public class Solution {
public int minPathSum ( int [ ] [ ] matrix) {
if ( matrix== null || matrix. length== 0 ) return 0 ;
int n= matrix. length;
int m= matrix[ 0 ] . length;
int [ ] [ ] dp= new int [ n+ 1 ] [ m+ 1 ] ;
for ( int i= 1 ; i<= n; i++ ) {
for ( int j= 1 ; j<= m; j++ ) {
if ( i== 1 ) {
dp[ i] [ j] = dp[ i] [ j- 1 ] + matrix[ i- 1 ] [ j- 1 ] ;
} else if ( j== 1 ) {
dp[ i] [ j] = dp[ i- 1 ] [ j] + matrix[ i- 1 ] [ j- 1 ] ;
} else {
dp[ i] [ j] = Math . min ( dp[ i- 1 ] [ j] , dp[ i] [ j- 1 ] ) + matrix[ i- 1 ] [ j- 1 ] ;
}
}
}
return dp[ n] [ m] ;
}
}
BM69 把数字翻译成字符串
描述
有一种将字母编码成数字的方式:'a'->1, 'b->2', ... , 'z->26'。
现在给一串数字,返回有多少种可能的译码结果
数据范围:字符串长度满足0<n≤90
进阶:空间复杂度O(n),时间复杂度O(n)
示例1
输入:"12"
返回值:2
说明:2种可能的译码结果(”ab” 或”l”)
示例2
输入:"31717126241541717"
返回值:192
说明:192种可能的译码结果
动态规划:
四种情况
1 无法翻译 当前为0 前面的>2或前面也为0
2 只能组合翻译 当前为0 前面为1或2
3 只能单独翻译 当前>6 前面为0或者前面>2
4 可以组合翻译 dp[i]=dp[i-1]+dp[i-2]
import java. util. ;
public class Solution {
public int solve ( String nums) {
if ( nums== null || nums. length ( ) == 0 ) return 0 ;
int [ ] dp = new int [ nums. length ( ) + 1 ] ;
dp[ 0 ] = 1 ;
dp[ 1 ] = nums. charAt ( 0 ) == '0' ? 0 : 1 ;
for ( int i= 2 ; i< dp. length; i++ ) {
if ( nums. charAt ( i- 1 ) == '0' && ( nums. charAt ( i- 2 ) == '0' || nums. charAt ( i- 2 ) > '2' ) ) {
return 0 ;
} else if ( nums. charAt ( i- 1 ) == '0' ) {
dp[ i] = dp[ i- 2 ] ;
} else if ( nums. charAt ( i- 2 ) == '0' || nums. charAt ( i- 2 ) > '2' || nums. charAt ( i- 2 ) == '2' && nums. charAt ( i- 1 ) > '6' ) {
dp[ i] = dp[ i- 1 ] ;
} else {
dp[ i] = dp[ i- 1 ] + dp[ i- 2 ] ;
}
}
return dp[ nums. length ( ) ] ;
}
}
BM70 兑换零钱(一)
描述
给定数组arr,arr中所有的值都为正整数且不重复。每个值代表一种面值的货币,每种面值的货币可以使用任意张,再给定一个aim,代表要找的钱数,求组成aim的最少货币数。 如果无解,请返回-1.
数据范围:数组大小满足0≤n≤10000 , 数组中每个数字都满足0<val≤10000,0≤aim≤5000
要求:时间复杂度O(n×aim) ,空间复杂度O(aim)
示例1
输入:[5,2,3],20
返回值:4
示例2
输入:[5,2,3],0
返回值:0
示例3
输入:[3,5],2
返回值:-1
思路:
动态规划
dp[i] = Math.min(dp[i],dp[i-arr[j]] + 1);
import java. util. ;
public class Solution {
public int minMoney ( int [ ] arr, int aim) {
int Max = aim + 1 ;
int [ ] dp = new int [ aim + 1 ] ;
Arrays . fill ( dp, Max ) ;
dp[ 0 ] = 0 ;
for ( int i = 1 ; i <= aim; i ++ ) {
for ( int j = 0 ; j < arr. length; j ++ ) {
if ( arr[ j] <= i) {
dp[ i] = Math . min ( dp[ i] , dp[ i- arr[ j] ] + 1 ) ;
}
}
}
return dp[ aim] > aim ? - 1 : dp[ aim] ;
}
}
BM71 最长上升子序列(一)
描述
给定一个长度为 n 的数组 arr,求它的最长严格上升子序列的长度。
所谓子序列,指一个数组删掉一些数(也可以不删)之后,形成的新数组。例如 [1,5,3,7,3] 数组,其子序列有:[1,3,3]、[7] 等。但 [1,6]、[1,3,5] 则不是它的子序列。
我们定义一个序列是 严格上升 的,当且仅当该序列不存在两个下标 i 和 j 满足 i<j 且 arri≥arrj。
数据范围: 0≤n≤1000
要求:时间复杂度 O(n^2), 空间复杂度 O(n)
示例1
输入:[6,3,1,5,2,3,7]
返回值:4
说明:该数组最长上升子序列为 [1,2,3,7] ,长度为4
思路:
动态规划
遍历0到i-1位置的元素,如果有小于i位置的元素
则dp[i]=Math.max(dp[i],dp[j]+1);
返回最大值即可
import java. util. ;
public class Solution {
public int LIS ( int [ ] arr) {
if ( arr== null || arr. length== 0 ) return 0 ;
int maxLen= 1 ;
int [ ] dp= new int [ arr. length] ;
Arrays . fill ( dp, 1 ) ;
for ( int i= 1 ; i< dp. length; i++ ) {
for ( int j= i- 1 ; j>= 0 ; j-- ) {
if ( arr[ i] > arr[ j] ) {
dp[ i] = Math . max ( dp[ i] , dp[ j] + 1 ) ;
}
}
maxLen= Math . max ( maxLen, dp[ i] ) ;
}
return maxLen;
}
}
BM72 连续子数组的最大和
描述
输入一个长度为n的整型数组array,数组中的一个或连续多个整数组成一个子数组,子数组最小长度为1。求所有子数组的和的最大值。
数据范围:1<=n<=2 10^5 , −100<=a[i]<=100
要求:时间复杂度为 O(n),空间复杂度为 O(n)
进阶:时间复杂度为 O(n),空间复杂度为 O(1)
示例1
输入:[1,-2,3,10,-4,7,2,-5]
返回值:18
说明:经分析可知,输入数组的子数组[3,10,-4,7,2]可以求得最大和为18
示例2
输入:[2]
返回值:2
示例3
输入:[-10]
返回值:-10
思路1:
动态规划 dp[i]=array[i-1]+((dp[i-1]>0)?dp[i-1]:0);
时间复杂度O(n) 空间复杂度 O(n)
思路2:
思路1的改进,dp状态只需要1个,前一个元素的dp值
public class Solution {
public int FindGreatestSumOfSubArray ( int [ ] array) {
if ( array== null || array. length== 0 ) return 0 ;
int [ ] dp= new int [ array. length+ 1 ] ;
int maxres= Integer . MIN_VALUE;
for ( int i= 1 ; i<= array. length; i++ ) {
dp[ i] = array[ i- 1 ] + ( ( dp[ i- 1 ] > 0 ) ? dp[ i- 1 ] : 0 ) ;
maxres= Math . max ( maxres, dp[ i] ) ;
}
return maxres;
}
}
public class Solution {
public int FindGreatestSumOfSubArray ( int [ ] array) {
if ( array== null || array. length== 0 ) return 0 ;
int b= 0 ;
int maxSum= Integer . MIN_VALUE;
for ( int i= 0 ; i< array. length; i++ ) {
b= array[ i] + ( b> 0 ? b: 0 ) ;
maxSum= Math . max ( b, maxSum) ;
}
return maxSum;
}
}
BM73 最长回文子串
描述
对于长度为n的一个字符串A(仅包含数字,大小写英文字母),请设计一个高效算法,计算其中最长回文子串的长度。数据范围:1≤n≤1000
要求:空间复杂度 O(1),时间复杂度 O(n^2)
进阶: 空间复杂度 O(n),时间复杂度 O(n)
示例1
输入:"ababc"
返回值:3
说明:最长的回文子串为"aba"与"bab",长度都为3
示例2
输入:"abbba"
返回值:5
示例3
输入:"b"
返回值:1
思路1:
中心扩散法,需要判断 双数和单数两种情况
时间复杂度:O(N^2),空间复杂度:O(1)
思路2:
双层for循环+双指针
时间复杂度:O(N^2),空间复杂度:O(1)
思路3:
动态规划
两层for循环
dp[left][right] = dp[left + 1][right - 1];
时间复杂度:O(N^2),空间复杂度:O(N^2)
import java. util. ;
public class Solution {
public int getLongestPalindrome ( String A ) {
char [ ] chars= A . toCharArray ( ) ;
int maxLength= 0 ;
for ( int i= 0 ; i< chars. length; i++ ) {
maxLength= Math . max ( maxLength, getLength ( i, chars) ) ;
}
return maxLength;
}
public int getLength ( int i, char [ ] chars) {
int res= 1 , left= i- 1 , right= i+ 1 ;
while ( left>= 0 && right< chars. length) {
if ( chars[ left] == chars[ right] ) {
res+= 2 ;
left-- ;
right++ ;
} else {
break ;
}
}
int res0= 0 ;
if ( ( i+ 1 ) < chars. length && chars[ i] == chars[ i+ 1 ] ) {
left= i;
right= i+ 1 ;
while ( left>= 0 && right< chars. length) {
if ( chars[ left] == chars[ right] ) {
res0+= 2 ;
left-- ;
right++ ;
} else {
break ;
}
}
}
return Math . max ( res, res0) ;
}
}
import java. util. ;
public class Solution {
public int getLongestPalindrome ( String A ) {
char [ ] chars= A . toCharArray ( ) ;
int maxlength= 0 ;
for ( int i= 0 ; i< chars. length; i++ ) {
for ( int j= i; j< chars. length; j++ ) {
if ( judge ( i, j, chars) ) maxlength= Math . max ( maxlength, j- i+ 1 ) ;
}
}
return maxlength;
}
public boolean judge ( int i, int j, char [ ] chars) {
if ( i== j) return true ;
while ( i< j) {
if ( chars[ i] != chars[ j] ) return false ;
i++ ;
j-- ;
}
return true ;
}
}
public int getLongestPalindrome ( String A , int n) {
if ( n < 2 ) return A . length ( ) ;
int maxLen = 1 ;
boolean [ ] [ ] dp = new boolean [ n] [ n] ;
for ( int right = 1 ; right < n; right++ ) {
for ( int left = 0 ; left <= right; left++ ) {
if ( A . charAt ( left) != A . charAt ( right) ) continue ;
if ( right == left) {
dp[ left] [ right] = true ;
} else if ( right - left <= 2 ) {
dp[ left] [ right] = true ;
} else {
dp[ left] [ right] = dp[ left + 1 ] [ right - 1 ] ;
}
if ( dp[ left] [ right] && right - left + 1 > maxLen) {
maxLen = right - left + 1 ;
}
}
}
return maxLen;
}
BM74 数字字符串转化成IP地址
描述
现在有一个只包含数字的字符串,将该字符串转化成IP地址的形式,返回所有可能的情况。
例如:
给出的字符串为"25525522135",
返回["255.255.22.135", "255.255.221.35"]. (顺序没有关系)
数据范围:字符串长度 0≤n≤12 要求:空间复杂度O(n!),时间复杂度 O(n!)
注意:ip地址是由四段数字组成的数字序列,格式如 "x.x.x.x",其中 x 的范围应当是 [0,255]。
示例1
输入:"25525522135"
返回值:["255.255.22.135","255.255.221.35"]
示例2
输入:"1111"
返回值:["1.1.1.1"]
示例3
输入:"000256"
返回值:"[]"
思路1:
暴力枚举,每一个位包含一到三位字符
如果四个位置相加为s.length
思路2:
递归回溯
思路1 :
import java. util. ;
public class Solution {
public ArrayList < String > restoreIpAddresses ( String s) {
ArrayList < String > list = new ArrayList ( ) ;
for ( int a= 1 ; a< 4 ; a++ ) {
for ( int b= 1 ; b< 4 ; b++ ) {
for ( int c= 1 ; c< 4 ; c++ ) {
for ( int d= 1 ; d< 4 ; d++ ) {
if ( a+ b+ c+ d== s. length ( ) ) {
String s1 = s. substring ( 0 , a) ;
String s2 = s. substring ( a, a+ b) ;
String s3 = s. substring ( a+ b, a+ b+ c) ;
String s4 = s. substring ( a+ b+ c, a+ b+ c+ d) ;
if ( check ( s1) && check ( s2) && check ( s3) && check ( s4) ) {
String ip = s1+ "." + s2+ "." + s3+ "." + s4;
list. add ( ip) ;
}
}
}
}
}
}
return list;
}
public boolean check ( String s) {
if ( Integer . valueOf ( s) <= 255 ) {
if ( s. charAt ( 0 ) != '0' || s. charAt ( 0 ) == '0' && s. length ( ) == 1 )
return true ;
}
return false ;
}
}
思路2 :
import java. util. ;
public class Solution {
public ArrayList < String > restoreIpAddresses ( String s) {
if ( s. length ( ) > 12 ) {
return null ;
}
StringBuilder sb = new StringBuilder ( s) ;
doRestoreIpAddresses ( sb, 1 ) ;
return ans;
}
ArrayList < String > ans = new ArrayList < > ( ) ;
public void doRestoreIpAddresses ( StringBuilder s, int m) {
String [ ] nums = s. toString ( ) . split ( "\\." ) ;
if ( nums. length > 4 ) {
return ;
}
if ( validateIPv4 ( s. toString ( ) ) ) {
ans. add ( String . copyValueOf ( s. toString ( ) . toCharArray ( ) ) ) ;
return ;
}
for ( int i = m; i < s. length ( ) ; i++ ) {
s. insert ( i, "." ) ;
doRestoreIpAddresses ( s, i + 2 ) ;
s. replace ( i, i + 1 , "" ) ;
}
}
public boolean validateIPv4 ( String IP) {
String [ ] nums = IP. split ( "\\." ) ;
if ( nums. length != 4 ) {
return false ;
}
for ( String x : nums) {
if ( x. length ( ) == 0 || x. length ( ) > 3 ) return false ;
if ( x. charAt ( 0 ) == '0' && x. length ( ) != 1 ) return false ;
if ( Integer . parseInt ( x) > 255 ) return false ;
}
return true ;
}
}
BM75 编辑距离(一)
描述
给定两个字符串 str1 和 str2 ,请你算出将 str1 转为 str2 的最少操作数。
你可以对字符串进行3种操作:
1.插入一个字符
2.删除一个字符
3.修改一个字符。
字符串长度满足1≤n≤1000,保证字符串中只出现小写英文字母。
示例1
输入:"nowcoder","new"
返回值:6
说明:"nowcoder"=>"newcoder"(将'o'替换为'e'),修改操作1次 "nowcoder"=>"new"(删除"coder"),删除操作5次
示例2
输入:"intention","execution"
返回值:5
说明:一种方案为:因为2个长度都是9,后面的4个后缀的长度都为"tion",于是从"inten"到"execu"逐个修改即可
示例3
输入:"now","nowcoder"
返回值:5
思路:
动态规划
int[][] dp=new int[str1.length()+1][str2.length()+1];
当前位置字符相等
dp[i+1][j+1]=dp[i][j];
当前位置字符不相等
dp[i+1][j+1]=Math.min(insert,delete,replace)
int delete=dp[i+1][j]+1;
int insert=dp[i][j+1]+1;
int change=dp[i][j]+1;
import java. util. ;
public class Solution {
public int editDistance ( String str1, String str2) {
if ( str1. length ( ) == 0 || str2. length ( ) == 0 ) return str1. length ( ) == 0 ? str2. length ( ) : str1. length ( ) ;
int [ ] [ ] dp= new int [ str1. length ( ) + 1 ] [ str2. length ( ) + 1 ] ;
for ( int i= 0 ; i<= str1. length ( ) ; i++ ) {
dp[ i] [ 0 ] = i;
}
for ( int i= 0 ; i<= str2. length ( ) ; i++ ) {
dp[ 0 ] [ i] = i;
}
for ( int i= 0 ; i< str1. length ( ) ; i++ ) {
for ( int j= 0 ; j< str2. length ( ) ; j++ ) {
if ( str1. charAt ( i) == str2. charAt ( j) ) {
dp[ i+ 1 ] [ j+ 1 ] = dp[ i] [ j] ;
} else {
int delete= dp[ i+ 1 ] [ j] + 1 ;
int insert= dp[ i] [ j+ 1 ] + 1 ;
int change= dp[ i] [ j] + 1 ;
int min = change > insert ? insert : change;
min = delete > min ? min : delete;
dp[ i + 1 ] [ j + 1 ] = min;
}
}
}
return dp[ str1. length ( ) ] [ str2. length ( ) ] ;
}
}
BM76 正则表达式匹配
描述
请实现一个函数用来匹配包括'.'和' '的正则表达式。
1.模式中的字符'.'表示任意一个字符
2.模式中的字符' '表示它前面的字符可以出现任意次(包含0次)。
在本题中,匹配是指字符串的所有字符匹配整个模式。例如,字符串"aaa"与模式"a.a"和"ab ac a"匹配,但是与"aa.a"和"ab a"均不匹配
数据范围:
1.str 只包含从 a-z 的小写字母。
2.pattern 只包含从 a-z 的小写字母以及字符 . 和 ,无连续的 ' '。
3. 0≤str.length≤26
4. 0≤pattern.length≤26
示例1
输入:"aaa","a a"
返回值:true
说明:中间的 可以出现任意次的a,所以可以出现1次a,能匹配上
示例2
输入:"aad","c a d"
返回值:true
说明:因为这里 c 为 0 个,a被重复一次, 表示零个或多个a。因此可以匹配字符串 "aad"。
示例3
输入:"a",". "
返回值:true
说明:". " 表示可匹配零个或多个(' ')任意字符('.')
示例4
输入:"aaab","a a a c"
返回值:false
思路:
动态规划
1、 i和j处的字符相等 dp[i][j]=d[i-1][j-1]
2、 j处为'.' dp[i][j]=d[i-1][j-1]
3、 j处为' ' j-1处不为'.'
匹配成功
3.1 匹配0次 dp[i][j]=dp[i][j-2]
3.2 匹配1次 dp[i][j]=d[i][j-1]
3.3 匹配n次 d[i][j]=d[i-1][j]
匹配不成功
3.4 匹配0次 dp[i][j]=dp[i][j-2]
4、j-1处为'.' j处为' '
4.1 匹配0次 dp[i][j]=dp[i][j-2]
4.2 匹配1次 dp[i][j]=d[i][j-1]
4.3 匹配n次 d[i][j]=d[i-1][j]
import java. util. ;
public class Solution {
public boolean match ( String str, String pattern) {
boolean [ ] [ ] dp= new boolean [ str. length ( ) + 1 ] [ pattern. length ( ) + 1 ] ;
dp[ 0 ] [ 0 ] = true ;
for ( int i= 1 ; i<= pattern. length ( ) ; i++ ) {
if ( i >= 2 && pattern. charAt ( i - 1 ) == ' ' ) dp[ 0 ] [ i] = dp[ 0 ] [ i - 2 ] ;
}
for ( int i= 1 ; i<= str. length ( ) ; i++ ) {
for ( int j= 1 ; j<= pattern. length ( ) ; j++ ) {
if ( str. charAt ( i- 1 ) == pattern. charAt ( j- 1 ) ) {
dp[ i] [ j] = dp[ i- 1 ] [ j- 1 ] ;
} else if ( pattern. charAt ( j- 1 ) == '.' ) {
dp[ i] [ j] = dp[ i- 1 ] [ j- 1 ] ;
} else if ( pattern. charAt ( j- 1 ) == ' ' ) {
if ( j>= 2 && pattern. charAt ( j- 2 ) == '.' ) {
dp[ i] [ j] = dp[ i] [ j- 2 ] || dp[ i] [ j- 1 ] || dp[ i- 1 ] [ j] ;
} else {
if ( j>= 2 && str. charAt ( i- 1 ) == pattern. charAt ( j- 2 ) ) {
dp[ i] [ j] = dp[ i] [ j- 2 ] || dp[ i] [ j- 1 ] || dp[ i- 1 ] [ j] ;
} else {
dp[ i] [ j] = dp[ i] [ j- 2 ] ;
}
}
}
}
}
return dp[ str. length ( ) ] [ pattern. length ( ) ] ;
}
}
BM77 最长的括号子串
描述
给出一个长度为 n 的,仅包含字符 '(' 和 ')' 的字符串,计算最长的格式正确的括号子串的长度。
例1: 对于字符串 "(()" 来说,最长的格式正确的子串是 "()" ,长度为 2 .
例2:对于字符串 ")()())" , 来说, 最长的格式正确的子串是 "()()" ,长度为 4 .
字符串长度:0≤n≤5∗10 5
要求时间复杂度 O(n) ,空间复杂度 O(n).
示例1
输入:"(()"
返回值:2
示例2
输入:"(())"
返回值:4
思路:
动态规划
当前位置i为( dp为0
当前位置i为) 需要判断
1、i-1为( ,dp[i]=dp[i-2]+2
2、i-1不为(,i-dp[i-1]-1=='(' dp[i]=dp[i-dp[i-1]-2]+dp[i-1]+2
import java. util. ;
public class Solution {
public int longestValidParentheses ( String s) {
int maxans = 0 ;
int [ ] dp = new int [ s. length ( ) ] ;
for ( int i = 1 ; i < s. length ( ) ; i++ ) {
if ( s. charAt ( i) == ')' ) {
if ( s. charAt ( i - 1 ) == '(' ) {
dp[ i] = ( i >= 2 ? 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 ;
}
maxans = Math . max ( maxans, dp[ i] ) ;
}
}
return maxans;
}
}
BM78 打家劫舍(一)
描述
你是一个经验丰富的小偷,准备偷沿街的一排房间,每个房间都存有一定的现金,为了防止被发现,你不能偷相邻的两家,即,如果偷了第一家,就不能再偷第二家;如果偷了第二家,那么就不能偷第一家和第三家。
给定一个整数数组nums,数组中的元素表示每个房间存有的现金数额,请你计算在不被发现的前提下最多的偷窃金额。
数据范围:数组长度满足1 ≤n≤2 ×10 ^ 5 ,数组中每个值满足1 ≤num[ i] ≤5000
示例1
输入:[ 1 , 2 , 3 , 4 ]
返回值:6
说明:最优方案是偷第 2 ,4 个房间
示例2
输入:[ 1 , 3 , 6 ]
返回值:7
说明:最优方案是偷第 1 ,3 个房间
示例3
输入:[ 2 , 10 , 5 ]
返回值:10
说明:最优方案是偷第 2 个房间
思路:
动态规划
每天有两种状态 偷和不偷
偷 dp[i-1][1]+nums[i-1]; 左边不偷+今天
不偷 dp[i][1]=Math.max(dp[i-1][0],dp[i-1][1]); 昨天投和不偷的最大值
import java. util. ;
public class Solution {
public int rob ( int [ ] nums) {
int [ ] [ ] dp= new int [ nums. length+ 1 ] [ 2 ] ;
dp[ 0 ] [ 0 ] = 0 ;
dp[ 0 ] [ 1 ] = 0 ;
int max= 0 ;
for ( int i= 1 ; i<= nums. length; i++ ) {
dp[ i] [ 0 ] = dp[ i- 1 ] [ 1 ] + nums[ i- 1 ] ;
dp[ i] [ 1 ] = Math . max ( dp[ i- 1 ] [ 0 ] , dp[ i- 1 ] [ 1 ] ) ;
max= Math . max ( dp[ i] [ 0 ] , dp[ i] [ 1 ] ) ;
}
return max;
}
}
BM79 打家劫舍(二)
描述
你是一个经验丰富的小偷,准备偷沿湖的一排房间,每个房间都存有一定的现金,为了防止被发现,你不能偷相邻的两家,即,如果偷了第一家,就不能再偷第二家,如果偷了第二家,那么就不能偷第一家和第三家。沿湖的房间组成一个闭合的圆形,即第一个房间和最后一个房间视为相邻。
给定一个长度为n的整数数组nums,数组中的元素表示每个房间存有的现金数额,请你计算在不被发现的前提下最多的偷窃金额。
数据范围:数组长度满足1≤n≤2×10^5,数组中每个值满足1≤nums[i]≤5000
示例1
输入:[1,2,3,4]
返回值:6
说明:最优方案是偷第 2 4 个房间
示例2
输入:[1,3,6]
返回值:6
说明:由于 1 和 3 是相邻的,因此最优方案是偷第 3 个房间
动态规划
1 从1-(n-1)
2 从2-n
计算两边动态规划
import java. util. ;
public class Solution {
public int rob ( int [ ] nums) {
int [ ] [ ] dp= new int [ nums. length] [ 2 ] ;
dp[ 0 ] [ 0 ] = 0 ;
dp[ 0 ] [ 1 ] = 0 ;
int RobFirst = 0 ;
for ( int i= 1 ; i< nums. length; i++ ) {
dp[ i] [ 0 ] = dp[ i- 1 ] [ 1 ] + nums[ i- 1 ] ;
dp[ i] [ 1 ] = Math . max ( dp[ i- 1 ] [ 0 ] , dp[ i- 1 ] [ 1 ] ) ;
RobFirst = Math . max ( dp[ i] [ 0 ] , dp[ i] [ 1 ] ) ;
}
int noRobFirst= 0 ;
dp= new int [ nums. length] [ 2 ] ;
for ( int i= 1 ; i< nums. length; i++ ) {
dp[ i] [ 0 ] = dp[ i- 1 ] [ 1 ] + nums[ i] ;
dp[ i] [ 1 ] = Math . max ( dp[ i- 1 ] [ 0 ] , dp[ i- 1 ] [ 1 ] ) ;
noRobFirst= Math . max ( dp[ i] [ 0 ] , dp[ i] [ 1 ] ) ;
}
return Math . max ( RobFirst , noRobFirst) ;
}
}
BM80 买卖股票的最好时机(一)
描述
假设你有一个数组prices,长度为n,其中prices[i]是股票在第i天的价格,请根据这个价格数组,返回买卖股票能获得的最大收益
1.你可以买入一次股票和卖出一次股票,并非每天都可以买入或卖出一次,总共只能买入和卖出一次,且买入必须在卖出的前面的某一天
2.如果不能获取到任何利润,请返回0
3.假设买入卖出均无手续费
数据范围:0≤n≤10^5,0≤val≤10^4
要求:空间复杂度 O(1),时间复杂度 O(n)
示例1
输入:[8,9,2,5,4,7,1]
返回值:5
说明:在第3天(股票价格 = 2)的时候买入,在第6天(股票价格 = 7)的时候卖出,最大利润 = 7-2 = 5 ,不能选择在第2天买入,第3天卖出,这样就亏损7了;同时,你也不能在买入前卖出股票。
示例2
输入:[2,4,1]
返回值:2
示例3
输入:[3,2,1]
返回值:0
思路:
动态规划
今天持有=max(今天买入,昨天就持有)
今天不持有=max(昨天没有,昨天有今天卖出)
import java. util. ;
public class Solution {
public int maxProfit ( int [ ] prices) {
int [ ] [ ] dp= new int [ prices. length] [ 2 ] ;
dp[ 0 ] [ 0 ] = 0 ;
dp[ 0 ] [ 1 ] = - prices[ 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 ] , - prices[ i] ) ;
}
return dp[ prices. length- 1 ] [ 0 ] ;
}
}
import java. util. ;
public class Solution {
public int maxProfit ( int [ ] prices) {
if ( prices. length< 2 ) return 0 ;
int max= 0 ;
for ( int i= 0 ; i< prices. length- 1 ; i++ ) {
int n1= prices[ i] ;
for ( int j= i+ 1 ; j< prices. length; j++ ) {
max= Math . max ( max, prices[ j] - n1) ;
}
}
return max;
}
}
BM81 买卖股票的最好时机(二)
描述
假设你有一个数组prices,长度为n,其中prices[i]是某只股票在第i天的价格,请根据这个价格数组,返回买卖股票能获得的最大收益
1. 你可以多次买卖该只股票,但是再次购买前必须卖出之前的股票
2. 如果不能获取收益,请返回0
3. 假设买入卖出均无手续费
数据范围:1≤n≤1×10^5,1≤prices[i]≤10^4
要求:空间复杂度 O(n),时间复杂度 O(n)
进阶:空间复杂度 O(1),时间复杂度 O(n)
示例1
输入:[8,9,2,5,4,7,1]
返回值:7
说明:
在第1天(股票价格=8)买入,第2天(股票价格=9)卖出,获利9-8=1
在第3天(股票价格=2)买入,第4天(股票价格=5)卖出,获利5-2=3
在第5天(股票价格=4)买入,第6天(股票价格=7)卖出,获利7-4=3
总获利1+3+3=7,返回7
示例2
输入:[5,4,3,2,1]
返回值:0
说明:由于每天股票都在跌,因此不进行任何交易最优。最大收益为0。
示例3
输入:[1,2,3,4,5]
返回值:4
说明:第一天买进,最后一天卖出最优。中间的当天买进当天卖出不影响最终结果。最大收益为4。
备注:总天数不大于200000。保证股票每一天的价格在[1,100]范围内。
思路:动态规划
今天持有=max(昨天利润+今天买入,昨天就持有)
今天不持有=max(昨天没有,昨天有今天卖出)
import java. util. ;
public class Solution {
public int maxProfit ( int [ ] prices) {
int [ ] [ ] dp= new int [ prices. length] [ 2 ] ;
dp[ 0 ] [ 0 ] = 0 ;
dp[ 0 ] [ 1 ] = - prices[ 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 ] [ 0 ] ;
}
}
BM82 买卖股票的最好时机(三)
描述
假设你有一个数组prices,长度为n,其中prices[i]是某只股票在第i天的价格,请根据这个价格数组,返回买卖股票能获得的最大收益
1. 你最多可以对该股票有两笔交易操作,一笔交易代表着一次买入与一次卖出,但是再次购买前必须卖出之前的股票
2. 如果不能获取收益,请返回0
3. 假设买入卖出均无手续费
数据范围:1≤n≤10^5,股票的价格满足1≤val≤10^4
要求: 空间复杂度 O(n),时间复杂度 O(n)
进阶:空间复杂度 O(1),时间复杂度 O(n)
示例1
输入:[8,9,3,5,1,3]
返回值:4
说明:
第三天(股票价格=3)买进,第四天(股票价格=5)卖出,收益为2
第五天(股票价格=1)买进,第六天(股票价格=3)卖出,收益为2
总收益为4。
示例2
输入:[9,8,4,1]
返回值:0
示例3
输入:[1,2,8,3,8]
返回值:12
说明:第一笔股票交易在第一天买进,第三天卖出;第二笔股票交易在第四天买进,第五天卖出;总收益为12。
因最多只可以同时持有一只股票,所以不能在第一天进行第一笔股票交易的买进操作,又在第二天进行第二笔股票交易的买进操作(此时第一笔股票交易还没卖出),最后两笔股票交易同时在第三天卖出,也即以上操作不满足题目要求。
备注:总天数不大于200000。保证股票每一天的价格在[1,100]范围内。
思路:
5个状态:1)不操作2)第一次购买3)第一次卖出4)第二次购买5)第二次卖出
import java. util. ;
public class Solution {
public int maxProfit ( int [ ] prices) {
if ( prices. length == 0 ) return 0 ;
/
5 个状态:1 )不操作2 )第一次购买3 )第一次卖出4 )第二次购买5 )第二次卖出
dp[ i] [ j] 代表第i天状态为j时产生的最大收益
/
int [ ] [ ] dp = new int [ prices. length] [ 5 ] ;
dp[ 0 ] [ 1 ] = - prices[ 0 ] ;
dp[ 0 ] [ 3 ] = - prices[ 0 ] ;
for ( int i = 1 ; i< prices. length; i++ ) {
dp[ i] [ 0 ] = dp[ i - 1 ] [ 0 ] ;
dp[ i] [ 1 ] = Math . max ( dp[ i - 1 ] [ 1 ] , dp[ i - 1 ] [ 0 ] - prices[ i] ) ;
dp[ i] [ 2 ] = Math . max ( dp[ i - 1 ] [ 2 ] , dp[ i - 1 ] [ 1 ] + prices[ i] ) ;
dp[ i] [ 3 ] = Math . max ( dp[ i - 1 ] [ 3 ] , dp[ i - 1 ] [ 2 ] - prices[ i] ) ;
dp[ i] [ 4 ] = Math . max ( dp[ i - 1 ] [ 4 ] , dp[ i - 1 ] [ 3 ] + prices[ i] ) ;
}
return dp[ prices. length - 1 ] [ 4 ] ;
}
}