努力了那么多年,回头一望,几乎全是漫长的挫折和煎熬。对于大多数人的一生来说,顺风顺水只是偶尔,挫折、不堪、焦虑和迷茫才是主旋律。我们登上并非我们所选择的舞台,演出并非我们所选择的剧本。继续加油吧!
目录
1、斐波那契数列
题目链接:斐波那契数列_牛客题霸_牛客网
思路:f(n) = f(n-1) + f(n-2)
Java版:
public class Solution {
public int Fibonacci(int n) {
if(n==1 || n==2){
return 1 ;
}
return Fibonacci(n-1) + Fibonacci(n-2) ;
}
}
2、跳台阶
题目链接:跳台阶_牛客题霸_牛客网
思路:和斐波那契数列一样的思路,只不过出口不一样。
Java版:
public class Solution {
public int jumpFloor(int target) {
if(target==1 ){
return 1 ;
}
if(target==2){
return 2 ;
}
return jumpFloor(target-1) + jumpFloor(target-2) ;
}
}
public class Solution {
public int jumpFloor(int target) {
int [] ans = new int [target] ;
ans[0] = 1 ;
if(ans.length > 1)
ans[1] = 2 ;
for(int i=2; i<ans.length; i++){
ans[i] = ans[i-1] + ans[i-2] ;
}
return ans[target-1] ;
}
}
3、最小花费爬楼梯
题目链接:最小花费爬楼梯_牛客题霸_牛客网
思路:爬到当前楼梯的最小花费=min(上个楼梯的最小花费+费用,上上个楼梯的最小花费+费用)
Java版:
import java.util.*;
public class Solution {
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
*
* @param cost int整型一维数组
* @return int整型
*/
public int minCostClimbingStairs (int[] cost) {
// write code here
int [] dp = new int [cost.length+1] ;
dp[0] = dp[1] = 0 ;
for(int i=2; i<dp.length; i++){
dp[i] = Math.min(dp[i-1]+cost[i-1],dp[i-2]+cost[i-2]) ;
}
return dp[cost.length] ;
}
}
4、最长公共子序列(二)
题目链接:最长公共子序列(二)_牛客题霸_牛客网
思路:先求出最长公共子序列的长度然后从后到前找出最长公共子序列,最后翻转一下。
Java版:
import java.util.*;
public class Solution {
/**
* longest common subsequence
* @param s1 string字符串 the string
* @param s2 string字符串 the string
* @return string字符串
*/
public String LCS (String s1, String s2) {
// write code here
int n = s1.length() + 1, m = s2.length() + 1 ;
int [][] dp = new int [n][m] ;
for(int i=1; i<n; i++){
for(int j=1; j<m; j++){
if(s1.charAt(i-1) == s2.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]) ;
}
}
}
StringBuilder sb = new StringBuilder("") ;
int l1 = s1.length(), l2 = s2.length() ;
while(l1 > 0 && l2 > 0){
if(s1.charAt(l1-1) == s2.charAt(l2-1)){
sb.append(s1.charAt(l1-1)) ;
l1 -- ;
l2 -- ;
}else{
if(dp[l1-1][l2] > dp[l1][l2-1]){
l1 -- ;
}else{
l2 -- ;
}
}
}
if(sb.length() == 0){
return "-1" ;
}
return sb.reverse().toString() ;
}
}
5、 最长公共子串
题目链接:最长公共子串_牛客题霸_牛客网
思路:求出最长公共字串的长度,并记录下标,然后直接截取字符串就可以。
Java版:
import java.util.*;
public class Solution {
/**
* longest common substring
* @param str1 string字符串 the string
* @param str2 string字符串 the string
* @return string字符串
*/
public String LCS (String str1, String str2) {
// write code here
int n = str1.length() + 1, m = str2.length() + 1 ;
int [][] dp = new int [n][m] ;
int max = 0, pos = 0 ;
for(int i=1; i<n; i++){
for(int j=1; j<m; j++){
if(str1.charAt(i-1) == str2.charAt(j-1)){
dp[i][j] = dp[i-1][j-1] + 1 ;
}else{
dp[i][j] = 0 ;
}
if(dp[i][j] > max){
max = dp[i][j] ;
pos = i - 1 ;
}
}
}
return str1.substring(pos-max+1, pos+1) ;
}
}
6、不同路径的数目(一)
题目链接:不同路径的数目(一)_牛客题霸_牛客网
思路:dp[i][j] = dp[i-1][j] + dp[i][j-1]
Java版:
import java.util.*;
public class Solution {
/**
*
* @param m int整型
* @param n int整型
* @return int整型
*/
public int uniquePaths (int m, int n) {
// write code here
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] ;
}
}
7、矩阵的最小路径和
题目链接:矩阵的最小路径和_牛客题霸_牛客网
思路:dp[i][j] = min(dp[i-1][j],dp[i][j-1])+matrix[i][j]
Java版:
import java.util.*;
public class Solution {
/**
*
* @param matrix int整型二维数组 the matrix
* @return int整型
*/
public int minPathSum (int[][] matrix) {
// write code here
int n = matrix.length, m = matrix[0].length ;
int [][] dp = new int [n][m] ;
dp[0][0] = matrix[0][0] ;
for(int i=0; i<n; i++){
for(int j = 0; j<m; j++){
if(i==0 && j==0){
continue ;
}
if(i==0){
dp[i][j] = dp[i][j-1] + matrix[i][j] ;
}else if(j == 0){
dp[i][j] = dp[i-1][j] + matrix[i][j] ;
}else{
dp[i][j] = Math.min(dp[i-1][j], dp[i][j-1]) + matrix[i][j] ;
}
}
}
return dp[n-1][m-1] ;
}
}
8、把数字翻译成字符串
题目链接:把数字翻译成字符串_牛客题霸_牛客网
思路:凡是出现11-19,21-26的就有两种编码方式,其余的要么一种,要么0种。
Java版:
import java.util.*;
public class Solution {
/**
* 解码
* @param nums string字符串 数字串
* @return int整型
*/
public int solve (String nums) {
// write code here
if(nums.equals("0")){
return 0 ;
}
if(nums.equals("10") || nums.equals("20")){
return 1 ;
}
for(int i=1; i<nums.length(); i++){
if(nums.charAt(i) == '0'){
if(nums.charAt(i-1) != '1' && nums.charAt(i-1) != '2'){
return 0 ;
}
}
}
int [] dp = new int [nums.length()+1];
Arrays.fill(dp,1) ;
for(int i=2; i<dp.length; i++){
if((nums.charAt(i-2) == '1' && nums.charAt(i-1) != '0') || (nums.charAt(i-2)== '2' && nums.charAt(i-1) > '0' && nums.charAt(i-1) < '7')){
dp[i] = dp[i-1] + dp[i-2] ;
}else{
dp[i] = dp[i-1] ;
}
}
return dp[nums.length()] ;
}
}
9、兑换零钱
题目链接:兑换零钱(一)_牛客题霸_牛客网
思路:列举出1-aim的所有dp[i],递推公式:dp[i] = Math(dp[i], dp[j-arr[i]] + 1),时间复杂度O(n*aim)
Java版:
import java.util.*;
public class Solution {
/**
* 最少货币数
* @param arr int整型一维数组 the array
* @param aim int整型 the target
* @return int整型
*/
public int minMoney (int[] arr, int aim) {
// write code here
int [] dp = new int [aim+1] ;
Arrays.fill(dp, aim+1) ;
dp[0] = 0 ;
//要列举所有的1-aim才行,并且每一个都要从所有的里面递推出最小的种类数目
for(int j=1; j<=aim ; j++){
for(int i=0; i<arr.length; i++){
if(arr[i] <= j){
dp[j] = Math.min(dp[j], dp[j - arr[i]] + 1) ;
}
}
}
int result = dp[aim] > aim ? -1 : dp[aim] ;
return result ;
}
}
10、最长上升子序列(一)
题目链接:最长上升子序列(一)_牛客题霸_牛客网
思路:dp[i]表示以i结尾的最长上升子序列的长度,双层循环,arr[i] > arr[j] && dp[i] < dp[j]+1时候更新dp[i].
Java版:
import java.util.*;
public class Solution {
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
* 给定数组的最长严格上升子序列的长度。
* @param arr int整型一维数组 给定的数组
* @return int整型
*/
public int LIS (int[] arr) {
// write code here
int [] dp = new int [arr.length] ;
if(arr.length == 0){
return 0 ;
}
Arrays.fill(dp, 1) ;
int max = 1 ;
for(int i=1; i<arr.length; i++){
for(int j=0; j<i; j++){
if(arr[i] > arr[j] && dp[i] < dp[j]+1){
dp[i] = dp[j] + 1 ;
}
if(max < dp[i]){
max = dp[i] ;
}
}
}
return max ;
}
}
11、连续子数组的最大和
题目链接:连续子数组的最大和_牛客题霸_牛客网
思路:之前累加的数字大于0,才允许累加当当前,否则继续从当前开始累加,累加过程中的最大值就是连续子数组的最大和。
Java版:
public class Solution {
public int FindGreatestSumOfSubArray(int[] array) {
//最好保证时间复杂度为O(n),空间复杂度为O(1)
int sum = 0 ;
int max = -100 ;
for(int i=0; i<array.length; i++){
if(sum + array[i] > array[i]){
sum += array[i] ;
}else{
sum = array[i] ;
}
if(max < sum){
max = sum ;
}
}
return max ;
}
}
12、最长回文子串
题目链接:最长回文子串_牛客题霸_牛客网
思路:dp[i][j]表示从i到j的最长回文字串,不过要以长度作为外层循环才行。
Java版:
import java.util.*;
public class Solution {
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
*
* @param A string字符串
* @return int整型
*/
public int getLongestPalindrome (String A) {
// write code here
int max = 0 ;
int n = A.length() ;
boolean [][] dp = new boolean [n][n] ;
//dp[i][i+j]:i表示开始,j表示长度,从i到i+j的最长回文字串啊
for(int j = 0; j<n; j++){
for(int i=0; i+j<n; i++){
if(j==0){
dp[i][i+j] = true ;
}else if(j==1){
dp[i][i+j] = (A.charAt(i) == A.charAt(i+j)) ;
}else{
dp[i][i+j] = (A.charAt(i) == A.charAt(i+j) && dp[i+1][i+j-1]) ;
}
if(dp[i][i+j] && j+1>max){
max = j + 1 ;
}
}
}
return max ;
}
}
13、数字字符串转化成IP地址
思路:循环判断满足长度和ip地址要求的字符串加入集合即可。
Java版:
import java.util.*;
public class Solution {
/**
*
* @param s string字符串
* @return string字符串ArrayList
*/
public ArrayList<String> restoreIpAddresses (String s) {
// write code here
ArrayList<String> list = new ArrayList<>() ;
for(int i=1; i<=3; i++){
for(int j=1; j<=3; j++){
for(int k=1; k<=3; k++){
for(int l=1; l<=3; l++){
if(i+j+k+l==s.length()){
String s1 = s.substring(0,i) ;
String s2 = s.substring(i,i+j) ;
String s3 = s.substring(i+j, i+j+k) ;
String s4 = s.substring(i+j+k, i+j+k+l) ;
if(f(s1) && f(s2) && f(s3) && f(s4)){
String tmp = s1 + "." + s2 + "." + s3 + "." + s4 ;
list.add(tmp) ;
}
}
}
}
}
}
return list ;
}
public boolean f(String s){
if(s.length() == 1){
return true ;
}else if(s.charAt(0) != '0' && Integer.parseInt(s) >= 10 && Integer.parseInt(s) <= 255){
return true ;
}
return false ;
}
}
14、编辑距离(一)
题目链接:编辑距离(一)_牛客题霸_牛客网
思路:dp[i][j]表示从开始到str1[i],str2[j]的最小操作次数。
Java版:
import java.util.*;
public class Solution {
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
*
* @param str1 string字符串
* @param str2 string字符串
* @return int整型
*/
public int editDistance (String str1, String str2) {
// write code here
//dp[i][j]表示从起点开始到str1[i]与str2[j]的最小操作次数
int n1 = str1.length(), n2 = str2.length() ;
int [][] dp = new int [n1+1][n2+1] ;
for(int i=1; i<=n1; i++){
dp[i][0] = dp[i-1][0] + 1 ;
}
for(int j=1; j<=n2; j++){
dp[0][j] = dp[0][j-1] + 1 ;
}
for(int i=1; i<=n1; i++){
for(int j=1; j<=n2; j++){
if(str1.charAt(i-1) == str2.charAt(j-1)){
dp[i][j] = dp[i-1][j-1] ;
}else{
dp[i][j] = Math.min(dp[i-1][j-1],Math.min(dp[i-1][j], dp[i][j-1])) + 1 ;
}
}
}
return dp[n1][n2] ;
}
}
15、 最长的括号子串
题目链接:最长的括号子串_牛客题霸_牛客网
思路:借助栈实现,如果是左括号入栈,遇到右括号需要进行判断,若栈元素小于2,则直接先出栈,让当前元素下标入栈,反之直接出栈,并求出当前得最长括号字串。
Java版:
import java.util.*;
public class Solution {
/**
*
* @param s string字符串
* @return int整型
*/
public int longestValidParentheses (String s) {
// write code here
Stack <Integer> stack = new Stack<>() ;
stack.add(-1) ;
int ans = 0 ;
for(int i=0; i<s.length(); i++){
if(s.charAt(i) == '('){
stack.add(i) ;
}else{
if(stack.size() > 1){
stack.pop() ;
ans = Math.max(ans, i-stack.peek()) ;
}else{
stack.pop() ;
stack.add(i) ;
}
}
}
return ans ;
}
}
16、打家劫舍1
题目链接:打家劫舍(一)_牛客题霸_牛客网
思路:隔一家偷一家,这种情况,递推dp[i] = max(dp[i-1], dp[i-2]+nums[i]) ;
Java版:
import java.util.*;
public class Solution {
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
*
* @param nums int整型一维数组
* @return int整型
*/
public int rob (int[] nums) {
// write code here
int n = nums.length ;
int [] dp = new int [n] ;
dp[0] = nums[0] ;
if(n>1){
dp[1] = Math.max(nums[0], nums[1]) ;
}
for(int i=2; i<n; i++){
dp[i] = Math.max(dp[i-1], dp[i-2]+nums[i]) ;
}
return dp[n-1] ;
}
}
17、打家劫舍2
题目链接:打家劫舍(二)_牛客题霸_牛客网
思路:环状的圈,需要判断第一个是否取,分两种可能讨论即可。
Java版:
import java.util.*;
public class Solution {
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
*
* @param nums int整型一维数组
* @return int整型
*/
public int rob (int[] nums) {
// write code here
int n = nums.length ;
int [] dp = new int [n] ;
dp[0] = nums[0] ;
dp[1] = nums[0] ;
for(int i=2; i<n; i++){
if(i==n-1){
dp[i] = Math.max(dp[i-1], dp[i-2]) ;
}else{
dp[i] = Math.max(dp[i-1], dp[i-2]+nums[i]) ;
}
}
int ans = dp[n-1] ;
Arrays.fill(dp, 0) ;
dp[1] = nums[1] ;
for(int i=2; i<n; i++){
dp[i] = Math.max(dp[i-1], dp[i-2]+nums[i]) ;
}
int max = (ans > dp[n-1]) ? ans : dp[n-1] ;
return max ;
}
}
18、买卖股票的最好时机(一)
思路:用二维数组的持有和抛出思路更合理一点,比较通用。
Java版:
import java.util.*;
public class Solution {
/**
*
* @param prices int整型一维数组
* @return int整型
*/
public int maxProfit (int[] prices) {
// write code here
int n = prices.length ;
int [][] dp = new int [n][3] ;
dp[0][0] = 0 ;
dp[0][1] = -prices[0] ;
dp[0][2] = 0 ;
for(int i=1; i<n; 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]) ;
}
return dp[n-1][2] ;
}
}
19、 买卖股票的最好时机(二)
思路:还是老模板,只不过换成可以多次交易,递推表达式稍作修改,注:从1开始遍历
Java版:
import java.util.*;
public class Solution {
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
* 计算最大收益
* @param prices int整型一维数组 股票每一天的价格
* @return int整型
*/
public int maxProfit (int[] prices) {
// write code here
int n = prices.length ;
int [][] dp = new int [n][2] ;
dp[0][0] = 0 ;
dp[0][1] = -prices[0] ;
for(int i=1; i<n; 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[n-1][0] ;
}
}
20、买卖股票的最好时机(三)
思路:两次购票,dp[n][5]分别代表未持有、第一次持有与抛出、第二次持有与抛出。
Java版:
import java.util.*;
public class Solution {
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
* 两次交易所能获得的最大收益
* @param prices int整型一维数组 股票每一天的价格
* @return int整型
*/
public int maxProfit (int[] prices) {
// write code here
int n = prices.length ;
int [][] dp = new int [n][5] ;
dp[0][0] = 0 ;
dp[0][1] = -prices[0] ;
dp[0][2] = dp[0][3] = dp[0][4] = -100000 ;
for(int i=1; i<n; 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[n-1][4] ;
}
}