给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那 两个 整数,并返回它们的数组下标。
你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。
你可以按任意顺序返回答案。
示例 1:
输入:nums = [2,7,11,15], target = 9
输出:[0,1]
解释:因为 nums[0] + nums[1] == 9 ,返回 [0, 1] 。
思路:查找问题:target-nums[i],比如target = 9,那我们只需要找比如9-2=7;我们要看数组里面有没有7;判断数组里有没有,第一时间想到hashmap提供的containsKey()方法,将target-nums[i]作为键,索引为值
注意:集合中存放的是要找的数,一旦命中直接返回
class Solution {
public int[] twoSum(int[] nums, int target) {
int res[] = new int[2];
HashMap<Integer,Integer> hp = new HashMap();
for(int i=0;i<nums.length;i++){
if(hp.containsKey(nums[i])){//找到了直接返回
res[0] = i;
res[1] = hp.get(nums[i]);
return res;
}
hp.put(target-nums[i],i);//没找到,加入hashmap
}
return res;
}
}
给你两个 非空 的链表,表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的,并且每个节点只能存储 一位 数字。
请你将两个数相加,并以相同形式返回一个表示和的链表。
输入:l1 = [2,4,3], l2 = [5,6,4]
输出:[7,0,8]
解释:342 + 465 = 807.
思路:每位数字逆序排列,则第一位是个位,依此类推十位百位…从个位开始做加法,产生的进位加到下一位,使用循环,循环条件为两链表不为空,当循环结束进位不为0那么将进位加入链表
class Solution {
public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
ListNode pre = new ListNode(0);//建立新链表头结点
ListNode cur = pre;//将头结点赋给cur用来操作新链表
int carry = 0;//进位
while(l1 != null || l2 != null) {
int x = l1 == null ? 0 : l1.val;
int y = l2 == null ? 0 : l2.val;
int sum = x + y + carry;
carry = sum / 10;
sum = sum % 10;
cur.next = new ListNode(sum);
cur = cur.next;
if(l1 != null)
l1 = l1.next;
if(l2 != null)
l2 = l2.next;
}
if(carry>0) {
cur.next = new ListNode(carry);
}
return pre.next;
}
}
给定一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a ,b ,c ,使得 a + b + c = 0 ?请找出所有和为 0 且 不重复 的三元组。
示例 1:
输入:nums = [-1,0,1,2,-1,-4]
输出:[[-1,-1,2],[-1,0,1]]
示例 2:
输入:nums = []
输出:[]
思路:做完了两数之和又来了三数之和,思路和两数差不多,先进行排序,然后固定一个数,然后找两个数和为该固定数的相反数
class Solution {
public List<List<Integer>> threeSum(int[] nums) {
if(nums == null||nums.length<3)return new ArrayList();
Arrays.sort(nums);
Set<List<Integer>> res = new HashSet<>();
for(int i=0;i<nums.length;i++){
int temp = -nums[i];
int left = i+1;
int right = nums.length-1;
while(left<right){
if(nums[left]+nums[right]==temp){
res.add(Arrays.asList(nums[i],nums[left],nums[right]));
left++;
right--;
}else if(nums[left]+nums[right]<temp){
left++;
}else{
right--;
}
}
}
return new ArrayList(res);
}
}
给定一个字符串 s
,请你找出其中不含有重复字符的 最长子串 的长度。
示例 1:
输入: s = "abcabcbb"
输出: 3
解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。
思路:一切尽在代码中
class Solution {
public int lengthOfLongestSubstring(String s) {
//定义左右指针,初始时指向同一位置,并定义最大长度,和当前长度
int left=0,right=0,len=0,maxlen=0;
HashMap<Character,Integer> hp = new HashMap();//使用map来存储字符串
for(right=0;right<s.length();right++){//遍历字符串
char c = s.charAt(right);
if(!hp.containsKey(c)){//如果集合中没有该字符,加进去,并将len加1
hp.put(c,right);
len++;
if(len>maxlen){
maxlen=len;
}
//如果集合中有,那么右移左指针,并删除对应元素,直到可以让右指针所指元素加进去
hp.remove(s.charAt(left));
left++;
len--;//删除元素后长度--
}
}
return maxlen;
}
}
给定两个整数 a 和 b ,求它们的除法的商 a/b ,要求不得使用乘号 ‘*’、除号 ‘/’ 以及求余符号 ‘%’ 。
注意:
整数除法的结果应当截去(truncate)其小数部分,例如:truncate(8.345) = 8 以及 truncate(-2.7335) = -2
假设我们的环境只能存储 32 位有符号整数,其数值范围是 [−231, 231−1]。本题中,如果除法结果溢出,则返回 231 − 1
示例 1:
输入:a = 15, b = 2
输出:7
解释:15/2 = truncate(7.5) = 7
示例 2:
输入:a = 7, b = -3
输出:-2
解释:7/-3 = truncate(-2.33333..) = -2
思路:该题可采用做减法,注意边界问题:
a=7,b=2;
让a每次减去b直到a
7-2=5 5>2 5-2=3 3>2 3-2=1 1<2 这样统计做了几次减法即可(正数) 优化:减倍数 每次减去b的倍数 7-2=5 减掉1个2 k=1 7-(2+2)=3 k=k+k=2 每次等于上一轮的k相加 ,7-((2+2)*2)<0,那么将a变成3,3-2=1<2 k=1 再优化:位运算 a=22,b=3 22-3<<0=19>0 k=1<<0=1 22-3<<1=16>0 k=1<<2=4 22-3<<2=10>0 k=1<<3=8 22-3<<4<0 将a换成10 10-3<<0=7>0 k=1<<0=1 10-3<<1=4>0 k=1<<1=2 10-3<<2<0 换成4 4-3<<0=1>0 k=1<<0 4-3<<1<0 再优化:每次从最大位32位尝试,这样循环次数32次,时间复杂度达到o(1) 22-3<<31 22-3<<32 …
class Solution {
public int divide(int a, int b) {
//32位最大值2147483648
//32位最小值-2147483648
//-2147483648/-1=2147483648越界了
if(a==Integer.MIN_VALUE&&b==-1){
return Integer.MAX_VALUE;
}
//将a,b取为正数
int a1=a<0?-a:a;
int b1=b<0?-b:b;//最小值-2147483648不会改变,下面采用<<<
int res=0;
for(int i=31;i>=0;i--){
if((a1>>>i)-b1>=0){//如果这里使用a>=b<
a1-=b1<<i;
res=res+(1<<i);
}
}
if((a>0&&b<0)||(a<0&&b>0)){//如果a,b中有一个为负数
return -res;
}else{
return res;
}
}
}
2.二进制加法
给定两个 01 字符串 a 和 b ,请计算它们的和,并以二进制字符串的形式输出。
输入为 非空 字符串且只包含数字 1 和 0。
示例 1:
输入: a = "11", b = "10"
输出: "101"
示例 2:
输入: a = "1010", b = "1011"
输出: "10101"
题解:该题和上面链表相加很像,对位相加后可能会产生进位
class Solution {
public String addBinary(String a, String b) {
StringBuilder sb=new StringBuilder();
int i=a.length()-1,j=b.length()-1,c=0;
while(i>=0||j>=0||c!=0){
int ii=i>=0?a.charAt(i--)-'0':0;
int jj=j>=0?b.charAt(j--)-'0':0;
c=ii+jj+c;
sb.append(c%2);
c/=2;
}
return sb.reverse().toString();
}
}
给定一个非负整数 n ,请计算 0 到 n 之间的每个数字的二进制表示中 1 的个数,并输出一个数组。
示例 1:
输入: n = 2
输出: [0,1,1]
解释:
0 --> 0
1 --> 1
2 --> 10
示例 2:
输入: n = 5
输出: [0,1,1,2,1,2]
解释:
0 --> 0
1 --> 1
2 --> 10
3 --> 11
4 --> 100
5 --> 101
思路:对于所有的数字,只有奇数和偶数两种:
奇数:二进制表示中,奇数一定比前面那个偶数多一个 1,因为多的就是最低位的 1
例如:5->101,4->100
偶数:二进制表示中,偶数中 1 的个数一定和除以 2 之后的那个数一样多。因为最低位是 0,除以 2 就是右移一位,也就是把那个 0 抹掉而已,所以 1 的个数是不变的。
12->1100,6->110,6=12>>1
所以该题可使用位运算+动态规划
dp[i]=dp[i-1]i为奇数
dp[i]=dp[i/2]i为偶数
对上面方程做出优化:
dp[i]=dp[i/2]+i%2=>dp[i]=dp[i>>1]+i%2
class Solution {
public int[] countBits(int n) {
int[] res = new int[n+1];
res[0] = 0;
for(int i = 1; i <= n; i++){
res[i] = res[i>>1] + i % 2;
}
return res;
}
}
给你一个整数数组 nums ,除某个元素仅出现 一次 外,其余每个元素都恰出现 三次 。请你找出并返回那个只出现了一次的元素。
示例 1:
输入:nums = [2,2,3,2]
输出:3
示例 2:
输入:nums = [0,1,0,1,0,1,100]
输出:100
思路:这种问题第一时间想到的大概就是hash表了,将其放入hash表记录其出现的次数
class Solution {
public int singleNumber(int[] nums) {
HashMap<Integer,Integer> map = new HashMap<>();
for(int i = 0;i<nums.length;i++){
map.put(nums[i],map.getOrDefault(nums[i],0)+1);
}
for(Map.Entry<Integer,Integer> entry:map.entrySet()){
if(entry.getValue() == 1)
return entry.getKey();
}
return 0;
}
}
使用位运算:
class Solution {
//将整数的各个数位上的加起来,然后对3取余,若结果为0,则待求数字在该位上是0;
//若结果为1,则待求数字在该位上是1.
public int singleNumber(int[] nums) {
//java的int整型为32位
int[] arr=new int[32];
for(int num:nums){
for(int i=0;i<32;i++){
arr[i]+=(num>>(31-i))&1;
}
}
int res=0;
for(int i=0;i<32;i++){
res=(res<<1)+arr[i]%3;
}
return res;
}
}
给定一个字符串数组 words,请计算当两个字符串 words[i] 和 words[j] 不包含相同字符时,它们长度的乘积的最大值。假设字符串中只包含英语的小写字母。如果没有不包含相同字符的一对字符串,返回 0。
示例 1:
输入: words = ["abcw","baz","foo","bar","fxyz","abcdef"]
输出: 16
解释: 这两个单词为 "abcw", "fxyz"。它们不包含相同字符,且长度的乘积最大。
示例 2:
输入: words = ["a","ab","abc","d","cd","bcd","abcd"]
输出: 4
解释: 这两个单词为 "ab", "cd"。
思路:该题判断单词相同可使用位运算;
int ->32位,总共有26个字母
int 一共32位我们使用26位从右开始依次向左代表a-z,字母存在该位为1不存在为0
相关知识:
class Solution {
public int maxProduct(String[] words) {
//先将每个单词转化位bit数组
int len = words.length;
int masks[] = new int[len];
for(int i=0;i<len;i++){
int bit = 0;//定义一个bit32位
for(char c : word[i].toCharArray()){
bit = bit | (1<<(c - 'a'));
}
masks[i] = bit;
}
int ans = 0;//最大结果
for(i = 0;i<len;i++){
String word1 = word[i].toCharArray();
for(j=j+1;j<len;j++){
if(masks[i] & masks[j] == 0){//相与为零说明不同
ans = Math.max(ans,word[i].length*word[j].length);
}
}
return ans;
}
}
}
给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那 两个 整数,并返回它们的数组下标。
你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。
你可以按任意顺序返回答案。
示例 1:
输入:nums = [2,7,11,15], target = 9
输出:[0,1]
解释:因为 nums[0] + nums[1] == 9 ,返回 [0, 1] 。
思路:对这道题进行优化以下
这次使用双指针,左指针从索引为0处开始,右指针从索引最大处开始
如果numbers[left]+numbers[right] < target;说明得让最小值变大一点;反之一样
class Solution {
public int[] twoSum(int[] numbers, int target) {
int left = 0,right = numbers.length-1;
int i = 0;
while(left<right){
if(numbers[left]+numbers[right] == target){//找到了,跳出循环
break;
}else if(numbers[left]+numbers[right] < target){
left++;
}else{
right--;
}
}
return new int[]{left,right};
}
}
给定一个含有 n 个正整数的数组和一个正整数 target 。
找出该数组中满足其和 ≥ target 的长度最小的 连续子数组 [numsl, numsl+1, …, numsr-1, numsr] ,并返回其长度。如果不存在符合条件的子数组,返回 0 。
示例 1:
输入:target = 7, nums = [2,3,1,2,4,3]
输出:2
解释:子数组 [4,3] 是该条件下的长度最小的子数组。
思路:利用双指针start ,end,分别指向子数组的头和尾,使用临时变量sum统计他们的和
class Solution {
//双指针法,类似于滑动窗口
public int minSubArrayLen(int target, int[] nums) {
//设置初始最小值
int min=Integer.MAX_VALUE;
//初始化数组和
int sum=0;
//定义两个变量i、j(类似于滑动窗口的感觉)
for(int i=0,j=0;j<nums.length;j++){
//扩大窗口
sum+=nums[j];
while(i<=j && sum>=target){
//更新最小值
min=Math.min(min,j-i+1);
//缩小窗口
sum-=nums[i++];
}
}
//若所有数组和都小于target,则返回0,否则返回更新值
return min==Integer.MAX_VALUE?0:min;
}
}
给定一个正整数数组 nums和整数 k ,请找出该数组内乘积小于 k 的连续的子数组的个数。
示例 1:
输入: nums = [10,5,2,6], k = 100
输出: 8
解释: 8 个乘积小于 100 的子数组分别为: [10], [5], [2], [6], [10,5], [5,2], [2,6], [5,2,6]。
需要注意的是 [10,5,2] 并不是乘积小于100的子数组。
思路: /**
* 思路: 设存在数组nums=[A, B, C, D], k为乘积, count为符合条件的数组个数, i,j为窗口左右边界;
*(假设) A: A
* B: AB
* C: ABC>=k j=2 --> BC
* D: BCD>k j=3 --> CD>K i=2 --> D < k i=3 --> count = D (3-3+1)
* 当计算的数组乘积大于k时,将窗口左边界右移, 直到小于k, 计算count,窗口右边界右移;
* 当计算的数组乘积小于k时,计算count,窗口右边界右移
* 得出规律:每一次遍历count增加了j-i+1
*/
class Solution {
public int numSubarrayProductLessThanK(int[] nums, int k) {
if(nums == null || k < 1){
return 0;
}
int i = 0, j = 0;
int res = 1, count = 0;
while(j < nums.length){
res *= nums[j];
while(i <= j && res >= k){
res /= nums[i++];
}
if(i <= j){
count += j - i + 1;
}
j++;
}
return count;
}
}
假设你是一位很棒的家长,想要给你的孩子们一些小饼干。但是,每个孩子最多只能给一块饼干。
对每个孩子 i,都有一个胃口值 g[i],这是能让孩子们满足胃口的饼干的最小尺寸;并且每块饼干 j,都有一个尺寸 s[j] 。如果 s[j] >= g[i],我们可以将这个饼干 j 分配给孩子 i ,这个孩子会得到满足。你的目标是尽可能满足越多数量的孩子,并输出这个最大数值。
示例 1:
输入: g = [1,2,3], s = [1,1]
输出: 1
解释:
你有三个孩子和两块小饼干,3个孩子的胃口值分别是:1,2,3。
虽然你有两块小饼干,由于他们的尺寸都是1,你只能让胃口值是1的孩子满足。
所以你应该输出1。
思路:把他们按照从小到大排序,然后遍历两个数组,如果 s[j] >= g[i],那给加过加1
class Solution {
public int findContentChildren(int[] g, int[] s) {
Arrays.sort(g);
Arrays.sort(s);
int res = 0,temp = 0;
while(res < g.length&&temp < s.length){
if(s[temp]>=g[res])res++;
temp++;
}
}
}
假设有一个很长的花坛,一部分地块种植了花,另一部分却没有。可是,花不能种植在相邻的地块上,它们会争夺水源,两者都会死去。
给你一个整数数组 flowerbed 表示花坛,由若干 0 和 1 组成,其中 0 表示没种植花,1 表示种植了花。另有一个数 n ,能否在不打破种植规则的情况下种入 n 朵花?能则返回 true ,不能则返回 false。
示例 1:
输入:flowerbed = [1,0,0,0,1], n = 1
输出:true
思路:经典跳格子:遍历如果遍历完了,或者n为0了结束,有三种情况
class Solution {
public boolean canPlaceFlowers(int[] flowerbed, int n) {
for(int i=0,len=flowerbed.length;i<len&&n>0;){
if(flowerbed[i]==1){
i=i+2;
}else if(i==len-1||flowerbed[i+1]==0){
i=i+2;
n--;
}else{
i=i+3;
}
}
return n<=0;
}
}
给你一个整数数组 nums ,找到其中最长严格递增子序列的长度。
子序列 是由数组派生而来的序列,删除(或不删除)数组中的元素而不改变其余元素的顺序。例如,[3,6,2,7] 是数组 [0,3,1,6,2,2,7] 的子序列。
示例 1:
输入:nums = [10,9,2,5,3,7,101,18]
输出:4
解释:最长递增子序列是 [2,3,7,101],因此长度为 4 。
思路:动态规划
dp[i]表示i之前包括以nums[i]结尾的最长上升子序列长度
转移方程:
当nums[i]>nums[j]时,dp[i]=max(dp[i],dp[j]+1)
class Solution {
public int lengthOfLIS(int[] nums) {
if(nums.length<=1)return nums.length;
int dp[]=new int[nums.length];
int res=0;
Arrays.fill(dp,1);
for(int i=1;i<nums.length;i++)
for(int j=0;j<i;j++){
if(nums[i]>nums[j]){
dp[i]=Math.max(dp[i],dp[j]+1);
}
if(dp[i]>res){
res=dp[i];
}
}
return res;
}
}
给定一个未经排序的整数数组,找到最长且 连续递增的子序列,并返回该序列的长度。
连续递增的子序列 可以由两个下标 l 和 r(l < r)确定,如果对于每个 l <= i < r,都有 nums[i] < nums[i + 1] ,那么子序列 [nums[l], nums[l + 1], …, nums[r - 1], nums[r]] 就是连续递增子序列。
示例 1:
输入:nums = [1,3,5,4,7]
输出:3
解释:最长连续递增序列是 [1,3,5], 长度为3。
尽管 [1,3,5,7] 也是升序的子序列, 但它不是连续的,因为 5 和 7 在原数组里被 4 隔开。
思路:
本题与上题不同之处在于连续
dp[i]表示以下标i为结尾的数组的连续递增的子序列长度为dp[i]
由于是连续的与上题不同这次不一定是以下标0开始
转移方程:
当nums[i]>nums[i-1]时,dp[i]=dp[i-1]+1;
class Solution {
public int findLengthOfLCIS(int[] nums) {
if(nums.length<=1)return nums.length;
int dp[]=new int[nums.length];
int res=0;
Arrays.fill(dp,1);//初始化数组长度为1,因为长度最短也为1
for(int i=1;i<dp.length;i++){
if(nums[i]>nums[i-1]){
dp[i]=dp[i-1]+1;
}
if(dp[i]>res){
res=dp[i];
}
}
return res;
}
}
当然本题也可使用贪心
class Solution {
public int findLengthOfLCIS(int[] nums) {
if(nums.length<=1)return nums.length;
int res=1;
int count=1;
for(int i=1;i<nums.length;i++){
//如果nums[i]>nums[i-1]那么给count+1
if(nums[i]>nums[i-1]){
count++;
}else{
//否则小于或者不连续将count置回1
count=1;
}
//每次更新res
if(res<count){
res=count;
}
}
return res;
}
}
概括来说:不连续递增子序列的跟前0-i 个状态有关,连续递增的子序列只跟前一个状态有关
给两个整数数组 nums1 和 nums2 ,返回 两个数组中 公共的 、长度最长的子数组的长度 。
示例 1:
输入:nums1 = [1,2,3,2,1], nums2 = [3,2,1,4,7]
输出:3
解释:长度最长的公共子数组是 [3,2,1] 。
思路:dp[i][j] :以下标i - 1为结尾的A,和以下标j - 1为结尾的B,最长重复子数组长度为dp[i][j]。 (特别注意: “以下标i - 1为结尾的A” 标明一定是 以A[i-1]为结尾的字符串 )
根据dp[i][j]的定义,dp[i][j]的状态只能由dp[i - 1][j - 1]推导出来。
即当A[i - 1] 和B[j - 1]相等的时候,dp[i][j] = dp[i - 1][j - 1] + 1;
根据递推公式可以看出,遍历i 和 j 要从1开始!
class Solution {
public int findLength(int[] nums1, int[] nums2) {
int res=0;
int dp[][]=new int[nums1.length+1][nums2.length+1];
for(int i=1;i<nums1.length+1;i++)
for(int j=1;j<nums2.length+1;j++){
if(nums1[i-1]==nums2[j-1]){
dp[i][j]=dp[i-1][j-1]+1;
res=Math.max(res,dp[i][j]);
}
}
return res;
}
}
给你一个整数数组 nums ,请你找出一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。
子数组 是数组中的一个连续部分。
示例 1:
输入:nums = [-2,1,-3,4,-1,2,1,-5,4]
输出:6
解释:连续子数组 [4,-1,2,1] 的和最大,为 6 。
思路:动态规划
将其分解为几个子问题:
子问题 1:以 -2−2 结尾的连续子数组的最大和是多少;
子问题 2:以 11 结尾的连续子数组的最大和是多少;
子问题 3:以 -3−3 结尾的连续子数组的最大和是多少;
子问题 4:以 44 结尾的连续子数组的最大和是多少;
子问题 5:以 -1−1 结尾的连续子数组的最大和是多少;
子问题 6:以 22 结尾的连续子数组的最大和是多少;
子问题 7:以 11 结尾的连续子数组的最大和是多少;
子问题 8:以 -5−5 结尾的连续子数组的最大和是多少;
子问题 9:以 44 结尾的连续子数组的最大和是多少。
可见问题1和问题2就是:当dp[i-1]>0时dp[i]=dp[i-1]+nums[i]
或者:dp[i]=Math.max(dp[i-1]+nums,nums)
class Solution {
public int maxSubArray(int[] nums) {
int res=nums[0];
//dp[i]表示前i个元素的最大和
int dp[]=new int[nums.length];
dp[0]=nums[0];
for(int i=1;i<nums.length;i++){
dp[i]=Math.max(nums[i],dp[i-1]+nums[i]);
res=Math.max(res,dp[i]);
}
return res;
}
}
给定两个字符串 text1 和 text2,返回这两个字符串的最长 公共子序列 的长度。如果不存在 公共子序列 ,返回 0 。
一个字符串的 子序列 是指这样一个新的字符串:它是由原字符串在不改变字符的相对顺序的情况下删除某些字符(也可以不删除任何字符)后组成的新字符串。
例如,“ace” 是 “abcde” 的子序列,但 “aec” 不是 “abcde” 的子序列。
两个字符串的 公共子序列 是这两个字符串所共同拥有的子序列。
示例 1:
输入:text1 = "abcde", text2 = "ace"
输出:3
解释:最长公共子序列是 "ace" ,它的长度为 3 。
dp[i][j]:长度为[0, i - 1]的字符串text1与长度为[0, j - 1]的字符串text2的最长公共子序列为dp[i][j]
有同学会问:为什么要定义长度为[0, i - 1]的字符串text1,定义为长度为[0, i]的字符串text1不香么?
这样定义是为了后面代码实现方便,如果非要定义为为长度为[0, i]的字符串text1也可以,大家可以试一试!
主要就是两大情况: text1[i - 1] 与 text2[j - 1]相同,text1[i - 1] 与 text2[j - 1]不相同
如果text1[i - 1] 与 text2[j - 1]相同,那么找到了一个公共元素,所以dp[i][j] = dp[i - 1][j - 1] + 1;
如果text1[i - 1] 与 text2[j - 1]不相同,那就看看text1[0, i - 2]与text2[0, j - 1]的最长公共子序列 和 text1[0, i - 1]与text2[0, j - 2]的最长公共子序列,取最大的。
即:dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);
class Solution {
public int maxUncrossedLines(int[] nums1, int[] nums2) {
int m=nums1.length,n=nums2.length;
int dp[][]=new int[m+1][n+1];
for(int i=1;i<=m;i++)
for(int j=1;j<=n;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][j-1],dp[i-1][j]);
}
}
return dp[m][n];
}
}
在两条独立的水平线上按给定的顺序写下 nums1 和 nums2 中的整数。
现在,可以绘制一些连接两个数字 nums1[i] 和 nums2[j] 的直线,这些直线需要同时满足满足:
nums1[i] == nums2[j]
且绘制的直线不与任何其他连线(非水平线)相交。
请注意,连线即使在端点也不能相交:每个数字只能属于一条连线。
以这种方法绘制线条,并返回可以绘制的最大连线数。
示例 1:
输入:nums1 = [1,4,2], nums2 = [1,2,4]
输出:2
解释:可以画出两条不交叉的线,如上图所示。
但无法画出第三条不相交的直线,因为从 nums1[1]=4 到 nums2[2]=4 的直线将与从 nums1[2]=2 到 nums2[1]=2 的直线相交。
class Solution {
public int maxUncrossedLines(int[] nums1, int[] nums2) {
int m=nums1.length,n=nums2.length;
int dp[][]=new int[m+1][n+1];
for(int i=1;i<=m;i++)
for(int j=1;j<=n;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][j-1],dp[i-1][j]);
}
}
return dp[m][n];
}
}
其实也就是说A和B的最长公共子序列是[1,4],长度为2。 这个公共子序列指的是相对顺序不变(即数字4在字符串A中数字1的后面,那么数字4也应该在字符串B数字1的后面)
这么分析完之后,大家可以发现:本题说是求绘制的最大连线数,其实就是求两个字符串的最长公共子序列的长度!
那么本题就和我们刚刚讲过的这道题目动态规划:1143.最长公共子序列 (opens new window)就是一样一样的了。
== nums2[j]
且绘制的直线不与任何其他连线(非水平线)相交。
请注意,连线即使在端点也不能相交:每个数字只能属于一条连线。
以这种方法绘制线条,并返回可以绘制的最大连线数。
示例 1:
[外链图片转存中…(img-xZSxVO5r-1660880847273)]
输入:nums1 = [1,4,2], nums2 = [1,2,4]
输出:2
解释:可以画出两条不交叉的线,如上图所示。
但无法画出第三条不相交的直线,因为从 nums1[1]=4 到 nums2[2]=4 的直线将与从 nums1[2]=2 到 nums2[1]=2 的直线相交。
class Solution {
public int maxUncrossedLines(int[] nums1, int[] nums2) {
int m=nums1.length,n=nums2.length;
int dp[][]=new int[m+1][n+1];
for(int i=1;i<=m;i++)
for(int j=1;j<=n;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][j-1],dp[i-1][j]);
}
}
return dp[m][n];
}
}
其实也就是说A和B的最长公共子序列是[1,4],长度为2。 这个公共子序列指的是相对顺序不变(即数字4在字符串A中数字1的后面,那么数字4也应该在字符串B数字1的后面)
这么分析完之后,大家可以发现:本题说是求绘制的最大连线数,其实就是求两个字符串的最长公共子序列的长度!
那么本题就和我们刚刚讲过的这道题目动态规划:1143.最长公共子序列 (opens new window)就是一样一样的了。
一样到什么程度呢? 把字符串名字改一下,其他代码都不用改,直接copy过来就行了。