坐标型动态规划之:Longest Increasing Subsequence
Posted 超级码厉
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了坐标型动态规划之:Longest Increasing Subsequence相关的知识,希望对你有一定的参考价值。
算法竞赛国家队成员
曾在美国Google、Airbnb担任Senior Staff Engineer
国内Top 1学校计算机毕业,硕士毕业时拿到10+美国顶级公司总部offer
现在旧金山自动驾驶start-up担任技术合伙人
篇尾送上小彩蛋:去美国做码农的途径。
上篇算法Minimum Sum Path,朋友们能把空间复杂度优化到O(min(m,n))吗?
昨天的Minimum Sum Path属于一道开胃菜,今天来一道稍微难一点的Longest Increasing Subsequence,但是套路还是一样的,先上题:
Longest Increasing Subsequence
Given an unsorted array of integers, find the length of longest increasing subsequence.
For example, Given [10, 9, 2, 5, 3, 7, 101, 18],
The longest increasing subsequence is [2, 3, 7, 101], therefore the length is 4. Note that there may be more than one LIS combination, it is only necessary for you to return the length.
Your algorithm should run in O(n2) complexity.
Follow up: Could you improve it to O(n log n) time complexity?
上题的意思是找出最长的增长序列的长度,首先说明下subsequence和substring的区别,即子序列和子字符串的区别。上述无序整型数组 [10, 9, 2, 5, 3, 7, 101, 18]中,
[10, 2, 3]组成的叫子序列,[10,9,2]组成的叫子字符串。一句话概括,子字符串中元素之间的坐标是相连的,子序列中元素之间的坐标可以跳跃。
思路分解如下:
假设有整型数组arr [10, 9, 2, 5, 3, 7, 101, 18]
(1)按照递归求解的思路,用f[i]代表以第i个元素结束时的最大长度。当i较小时,容易直观看出来,f[0] = 1, 自身占一个位置,所以距离是0;
(2) 那么,如何用已经得到f[0] ~ f[i-1]值推得后面的f[i]呢?
假设f[0] ~ f[i-1]的值都已经确定。注意到,以arr[i]结尾的递增子序列,除了长度为1的情况(自身一个元素,所以长度是1),其他情况中,arr[i] 都跟在一个以arr[j](j < i)递增的子序列之后。
要求以arr[i]结尾的最长子序列,需要依次比较arr[i]与其之前所有的arr[j]( j < i ), if arr[j] < arr[i], 则 arr[j] 可以跟在以arr[i]可以跟在以arr[j] 结尾的子序列之后,形成一个新的子序列。因为f[i] 代表以第i个元素结束时的最大子序列长度,所以,取上述一次遍历过程中的最大值即可,抽象出的状态方程如下:
f[i] = max{f[j] + 1}, j必须满足 j < i && nums[j] <= nums[i]
(3)是不是很简单? 利用初中学到的递归思路就能解出来的。
可以用一种的形象的思路把上述过程抽象成模版,如下:
将n个数看做n个木桩,目的是从某个木桩出发,从前向后,从低往高,看做多能踩多少个木桩。
state: f[i] 表示(从任意某个木桩)跳到第i个木桩,最多踩过多少根木桩
function: f[i] = max{f[j] + 1}, j必须满足 j < i && nums[j] <= nums[i]
initialize: f[0..n-1] = 1
answer: max{f[0..n-1]}
Reference: Nine Chapter Algrithm
Java实现:
public class Solution {
public int lengthOfLIS(int[] nums) {
if(nums == null || nums.length == 0) {
return 0;
}
int length = nums.length;
int[] dp = new int[length];
for(int i = 0; i < length; i++) {
dp[i] = 1;
}
int max = dp[0];
for(int i = 1; i < length; i++)
for(int j = 0; j < i; j++) {
if(nums[i] > nums[j]) {
dp[i] = Math.max(dp[j] + 1, dp[i]);
}
max = Math.max(max,dp[i]);
}
return max;
}
}
常见以下5种途径:
(1)美国读CS硕士, CPT、OPT次数加起来最毒五次,最后抽中H1B工作签证的工作机会很多;
(2)google 、 apple、 amazon、airbnb 等美资企业的中国分公司,一年之后申请transfer过去,持L1B签证,高级管理岗位持L1A签证,过去之后每年可以抽一次H1B工作签证;
(3)牛逼的大牛,在国际顶级期刊,某些领域有突出贡献的,如ACM奖牌获得者,进入ACM world final的选手, 可以肉身直接投递简历,本科毕业虽然抽中H1B的几率很低,但是这些大公司都珍惜人才,可以把你搞到加拿大、香港、英国等分公司,再逐渐转过去,也是走L1A的路子;
(4)正牌美国博士,也就是俗称的phd,美国移民局每年预留了5000个H1B签证给phd,毕业只要能拿到大公司offer,就不需要去摇号抽签拉。
(5)美国以外拿到博士学位的,抽H1B的几率也比较高。
当然,如果你是土豪,直接投资移民了,也不需要去做码农了不是。
美国公司面试流程:
(1)最好找校友、朋友内推,用好Facebook, LinkedIn, 一亩三分地等资源;
(2)提前准备好算法和系统设计;
备注: onsite都是在白板上写code,bug free的要求。 这个过程中要充分与面试官沟通,了解需求,表述自己的思路.
美帝工作要提前一到两年做好规划,真正执行时要集中搞两三月,牛人就另当别论了。
好了,下次分享一个医学专cs三个月拿到google offer的案例。
以上是关于坐标型动态规划之:Longest Increasing Subsequence的主要内容,如果未能解决你的问题,请参考以下文章
算法动态规划 ④ ( 动态规划分类 | 坐标型动态规划 | 前缀划分型动态规划 | 前缀匹配型动态规划 | 区间型动态规划 | 背包型动态规划 )
算法动态规划 ④ ( 动态规划分类 | 坐标型动态规划 | 前缀划分型动态规划 | 前缀匹配型动态规划 | 区间型动态规划 | 背包型动态规划 )