Arithmetic Slices II - Subsequence LT446
A sequence of numbers is called arithmetic if it consists of at least three elements and if the difference between any two consecutive elements is the same.
For example, these are arithmetic sequences:
1, 3, 5, 7, 9
7, 7, 7, 7
3, -1, -5, -9
The following sequence is not arithmetic.
1, 1, 2, 5, 7
A zero-indexed array A consisting of N numbers is given. A subsequence slice of that array is any sequence of integers (P0, P1, ..., Pk) such that 0 ≤ P0 < P1 < ... < Pk < N.
A subsequence slice (P0, P1, ..., Pk) of array A is called arithmetic if the sequence A[P0], A[P1], ..., A[Pk-1], A[Pk] is arithmetic. In particular, this means that k ≥ 2.
The function should return the number of arithmetic subsequence slices in the array A.
The input contains N integers. Every integer is in the range of -231 and 231-1 and 0 ≤ N ≤ 1000. The output is guaranteed to be less than 231-1.
Example:
Input: [2, 4, 6, 8, 10] Output: 7 Explanation:
All arithmetic subsequence slices are:
[2,4,6]
[4,6,8]
[6,8,10]
[2,4,6,8]
[4,6,8,10]
[2,4,6,8,10]
[2,6,10]
Idea 1. BruteForce. 穷举所有符合条件的序列,序列其实是数组的subsets, 用DepthFirst Search穷举subsets.
Time complexity: O(2^n) For each element in the array, it can be put in or outside of the subsequence, two choices for each element.
Space complexity: stack depth O(n)
class Solution {
private boolean isArithmeticSequence(int[] A, int currDep) {
if(currDep < 3) {
return false;
}
long diff = (long)A[1] - A[0];
for(int i = 2; i < currDep; ++i) {
if(diff != (long)A[i] - A[i-1]) {
return false;
}
}
return true;
}
private void helper(int[] A, int depth, int[] path, int pathPos, int[] count) {
if(depth == A.length) {
if(isArithmeticSequence(path, pathPos)) {
++count[0];
}
return;
}
helper(A, depth+1, path, pathPos, count);
path[pathPos] = A[depth];
helper(A, depth+1, path, pathPos+1, count);
}
public int numberOfArithmeticSlices(int[] A) {
int[] path = new int[A.length];
int[] count = new int[1];
helper(A, 0, path, 0, count);
return count[0];
}
}
Note: 1. reset the change on the current depth before backtracking to the previous depth. List implementation is more obvious, as array just keep the int index pathPos unchanged.
2. Overflow, change int to long to filter out invalid cases, as there is no valid arithmetic subsequence slice that can have difference out of the Integer value range.
class Solution {
private boolean isArithmeticSequence(List<Integer> curr) {
if(curr.size() < 3) {
return false;
}
long diff = (long)curr.get(1) - curr.get(0);
for(int i = 2; i < curr.size(); ++i) {
if(diff != (long)curr.get(i) - curr.get(i-1)) {
return false;
}
}
return true;
}
private void helper(int[] A, int depth, List<Integer> curr, int[] count) {
if(depth == A.length) {
if(isArithmeticSequence(curr)) {
++count[0];
}
return;
}
helper(A, depth+1, curr, count); // not put A[depth] in the subsequence
curr.add(A[depth]);
helper(A, depth+1, curr, count); // put A[depth] in the subsequence
curr.remove(curr.size()-1); // reset before backtracking
}
public int numberOfArithmeticSlices(int[] A) {
int[] count = new int[1];
helper(A, 0, new ArrayList<>(), count);
return count[0];
}
}
python:
class Solution:
def isArithmetic(self, curr: List[int]) -> bool:
if(len(curr) < 3):
return False; diff = float(curr[1]) - curr[0]
for i in range(2, len(curr)):
if diff != float(curr[i]) - curr[i-1]:
return False; return True def helper(self, A: List[int], depth: int, curr: List[int], count: List[int]) -> None :
if depth == len(A):
if self.isArithmetic(curr):
count[0] += 1 return self.helper(A, depth+1, curr, count)
curr.append(A[depth])
self.helper(A, depth+1, curr, count)
curr.pop() def numberOfArithmeticSlices(self, A: List[int]) -> int:
count = [0]
self.helper(A, 0, [], count)
return count[0]
Idea 2: Dynamic programming, similar to Arithmetic Slices LT413, how to extend from solution to nums[0...i] to nums[0..i, i+1]? LT413的sequence要求是连续的,只需要检查能否延续前一位为结尾的序列,一维的关系:dp(i) = dp(i-1) + 1; 而这一题可以跳过前面的数,延续前面任何以nums[j]结尾的满足条件的序列(0 <j <i, diff(nums[k, j]) = nums[i] - nums[j]),需要加入序列的差d来表达关系,用dp(i, d)表示以nums[i]结尾,序列差为d的序列个数,
dp(i, d) = sum(dp(j, d) + 1)
序列要求是三位数的长度,如果以3位数为base case这个并不好计算,如果放松一下条件2位数算作wealy arithmetic sequence, 上面的公式依然成立,2位数的base case也好计算,
dp(i, nums[i]-nums[j]) = 1 for any pair j, i, 0 <= j < i
我们来走一下例子:[1, 1, 2, 3, 4, 5]
i = 0, dp(0, d) = 0
i = 1, j = 0, diff = 1 - 1 = 0, dp(1, 0) = 1, sequence: [1, 1]
i = 2, j = 0, diff = 2 - 1 = 1, dp(2, 1) = 1; j = 1, diff = 2 - 1 = 1, dp(2, 1) = 1 + 1 = 2 sequence: [1, 2], [1, 2]
i = 3, j = 0, diff = 2, dp(3, 2) = 1; j = 1, diff = 2, dp(3, 2) = 2; j = 2, diff = 1, dp(3, 1) = dp(2, 1) + 1 = 3, sequence: [1, 3], [1, 3], [1, 2, 3], [1, 2, 3], [2, 3]
i = 4, j = 0, diff = 3, dp(4, 3) = 1; j = 1, diff = 3, dp(4, 3) = 2; j = 2, diff = 2, dp(4, 2) = 1; j = 3, dp(4, 1) = dp(3, 1) + 1 = 4, sequence: [1, 4], [1, 4], [2, 4], [1, 2, 3, 4], [1, 2, 3, 4], [2, 3, 4], [3, 4]
i = 5, j = 0, diff = 4, dp(5, 4) = 1, sequence[1, 5]; j = 1, diff = 4, dp(5, 4) = 2, sequence [1, 5] [1, 5]; j = 2, diff = 3, dp(5, 3) = 1; j = 3, diff = 2, dp(3, 2) = 2, dp(5, 2) = dp(3, 2) + 1 = 3, sequence [1, 3, 5], [1, 3, 5], [3, 5]; j = 4, dp(5, 1) = dp(4, 1) = 5, sequence [1, 2, 3, 4, 5], [1, 2, 3, 4, 5], [2, 3, 4, 5], [3, 4, 5], [4, 5]
从例子可以看出来符合至少3位数的序列个数其实取决于前面sequence个数dp(j, d), 公式中的+1是pair (nums[j], nums[i])2位数的序列,总结公式如下:
dp(i, d) = sum(dp(j, d) + 1)
dp(i, nums[i]-nums[j]) = 1 for any pair j, i, 0 <= j < i
result(3位数的序列个数) = sum(dp(j, d))
由于 d是unbounded可正可负,一般dynamic programming使用二维数组做memory就不能用了,而用array of map, dp(i).get(d) = dp(i, d)
Time complexity: O(n2)
Space complexity: O(n2)
class Solution {
public int numberOfArithmeticSlices(int[] A) {
int result = 0;
List<Map<Integer, Integer> > dp = new ArrayList();
for(int i = 0; i < A.length; ++i) {
dp.add(new HashMap());
for(int j = 0; j < i; ++j) {
long delta = (long)A[i] - A[j];
if(delta < Integer.MIN_VALUE || delta > Integer.MAX_VALUE) {
continue;
}
int diff = (int) delta;
int prev = dp.get(j).getOrDefault(diff, 0);
int curr = dp.get(i).getOrDefault(diff, 0);
dp.get(i).put(diff, curr + prev + 1);
result += prev;
}
}
return result;
}
}
array of map
class Solution {
public int numberOfArithmeticSlices(int[] A) {
int result = 0;
Map<Integer, Integer>[] dp = new Map[A.length];
for(int i = 0; i < A.length; ++i) {
dp[i] = new HashMap<>();
for(int j = 0; j < i; ++j) {
long delta = (long)A[i] - A[j];
if(delta < Integer.MIN_VALUE || delta > Integer.MAX_VALUE) {
continue;
}
int diff = (int) delta;
int prev = dp[j].getOrDefault(diff, 0);
int curr = dp[i].getOrDefault(diff, 0);
dp[i].put(diff, curr + prev + 1);
result += prev;
}
}
return result;
}
}
python:
class Solution:
def numberOfArithmeticSlices(self, A: List[int]) -> int:
dp = [{} for _ in range(len(A))]
result = 0
for i in range(len(A)):
for j in range(i):
delta = A[i] - A[j]
prev = dp[j].get(delta, 0)
curr = dp[i].get(delta, 0)
dp[i][delta]= curr + prev + 1
result += prev return result
Idea 3. 前面我们提到如果以3位数为base case这个并不好计算,换一个角度nums[i] - nums[j] = nums[j] - nums[k], 0 <= k < j < i, 如果有nums[k] = nums[j] * 2 - nums[i], 需要快速地找到nums[k],我们需要一个map记录nums[k] 和 index k.
dp[i][j] = sum(dp[j][k] + 1)
base case dp[i][j] = 0
result = sum(dp[i][j])
我们来走一下例子:[1, 1, 2, 3, 4, 5]
lookup(nums[k], [k]): 1-> [0, 1] , 2-> [2], 3-> [3], 4-> [4], 5-> [5]
i = 2, j = 1, nums[k] = 0, 不存在;
i = 3, j = 1, nums[k] = 2 * 1 - 3= -1,不存在; j= 2, nums[k] = 2 * 2 - 3 = 1, dp[3][2] += dp[2][0] + 1 + dp[2][1] + 1 = 2, sequence [1,2,3], [1, 2, 3]
i = 4, j = 1, nums[k] = 2 * 1 - 4 = -2, 不存在; j= 2, nums[k] = 2 * 2 - 4 = 0, 不存在; j = 3, nums[k] = 2 * 3 - 4 = 2, dp[4][3] += dp[3][2] + 1 = 3, sequence: [1,2,3, 4], [1, 2, 3, 4], [2, 3, 4]
i = 5, j = 1, nums[k] = 2 * 1 - 5 = -3, 不存在; j= 2, nums[k] = 2 * 2 - 5 = -1, 不存在; j = 3, nums[k] = 2 * 3 - 5 = 1, dp[5][3] = dp[3][1] + 1 + dp[3][0] + 1 = 2; j = 4, nums[k] = 2 * 4 - 5 = 3, dp[5][4] += dp[4][3] + 1 = 4, sequence: [1, 3, 5], [1, 3, 5], [1, 2, 3, 4, 5], [1, 2,3,4,5], [2, 3, 4, 5], [3, 4, 5]
Time complexity: O(n3) the worest case to loop the map lookup could be nearly as O(n), when have lots of duplicates like 1, 1, 1, 1, 2, 3, 4
Space complexity: O(n2)
class Solution {
public int numberOfArithmeticSlices(int[] A) {
int result = 0;
Map<Integer, List<Integer>> lookUp = new HashMap<>();
int[][] dp = new int[A.length][A.length];
for(int i = 0; i < A.length; ++i) {
if(lookUp.get(A[i]) == null) {
lookUp.put(A[i], new ArrayList<>());
}
lookUp.get(A[i]).add(i);
}
for(int i = 2; i < A.length; ++i) {
for(int j = 1; j < i; ++j) {
long tempTarget = 2 * (long)A[j] - A[i];
if(tempTarget < Integer.MIN_VALUE
|| tempTarget > Integer.MAX_VALUE) {
continue;
}
int target = (int) tempTarget;
if(lookUp.containsKey(target)) {
for(int k: lookUp.get(target)) {
if(k < j) {
dp[i][j] += dp[j][k] + 1;
}
}
result += dp[i][j];
}
}
}
return result;
}
}
python
class Solution:
def numberOfArithmeticSlices(self, A: List[int]) -> int:
result = 0
dp = [collections.defaultdict(int) for _ in range(len(A))]
lookup = collections.defaultdict(list) for i, val in enumerate(A):
lookup[val].append(i) for i in range(2, len(A)):
for j in range(1, i):
target = 2 * A[j] - A[i]
if target in lookup:
for k in lookup[target]:
if k < j:
dp[i][j] += dp[j][k] + 1 result += dp[i][j] return result
Arithmetic Slices II - Subsequence LT446的更多相关文章
- [LeetCode] Arithmetic Slices II - Subsequence 算数切片之二 - 子序列
A sequence of numbers is called arithmetic if it consists of at least three elements and if the diff ...
- LeetCode 446. Arithmetic Slices II - Subsequence
原题链接在这里:https://leetcode.com/problems/arithmetic-slices-ii-subsequence/ 题目: A sequence of numbers is ...
- Leetcode: Arithmetic Slices II - Subsequence
A sequence of numbers is called arithmetic if it consists of at least three elements and if the diff ...
- [Swift]LeetCode446. 等差数列划分 II - 子序列 | Arithmetic Slices II - Subsequence
A sequence of numbers is called arithmetic if it consists of at least three elements and if the diff ...
- LeetCode446. Arithmetic Slices II - Subsequence
A sequence of numbers is called arithmetic if it consists of at least three elements and if the diff ...
- 446. Arithmetic Slices II - Subsequence
A sequence of numbers is called arithmetic if it consists of at least three elements and if the diff ...
- 446 Arithmetic Slices II - Subsequence 算数切片之二 - 子序列
详见:https://leetcode.com/problems/arithmetic-slices-ii-subsequence/description/ C++: class Solution { ...
- 第六周 Leetcode 446. Arithmetic Slices II - Subsequence (HARD)
Leetcode443 题意:给一个长度1000内的整数数列,求有多少个等差的子数列. 如 [2,4,6,8,10]有7个等差子数列. 想了一个O(n^2logn)的DP算法 DP[i][j]为 对于 ...
- [LeetCode] Arithmetic Slices 算数切片
A sequence of number is called arithmetic if it consists of at least three elements and if the diffe ...
随机推荐
- Link & Redirect
[Link] Link标签,用于实现React-Router功能的跳转.(意思是就不要使用a标签了) 1)to:string,指明要跳转的path. import { Link } from 'rea ...
- zabbix web 配置
一.汉化 1.在windows中找一些自己喜欢的字体: 2.将字体上传至/var/www/html/zabbix/fonts 目录下(我上传的字体为:simhei.ttf) 3.在/var/www/h ...
- 关于 No buffer space available (maximum connections reached?): connect 的处理
一.问题: hudson一个应用打包部署一直不成功,检查报错 检查项目的JOB配置,开始以为是SVN的问题,但是重启SVN后问题一直存在 二.分析: TCP协议中,关闭TCP连接的是Server端(当 ...
- 游戏行业的女性拥有强大的新盟友:Facebook
据外媒 TheNextWeb 报道,Facebook 本周宣布其新的游戏行业女性倡议,致力于鼓励更多的女性加入游戏行业.这家社交媒体公司专注于提供榜样和成功故事,而这实际上是一种令人愉快的方式.虽然游 ...
- f5售后查询
登录: https://secure.f5.com/validate/validate.jsp http://boochem.blog.51cto.com/628505/633907
- Django的Rbac介绍2
上一篇博客我们记录了一下Django中使用Rbac,但是上一篇博客中的方法有一点不好,就是,因为我要在html文件中控制:如果用户有某个权限,则显示这个权限所代表的按钮,但是我现在只有1张表的增删改查 ...
- layui禁用侧边导航栏点击事件
layui是一款优秀的前端模块化css框架,作者是贤心 —— 国内的一位前端大佬. 我用layui做过两个完整的项目,对她的感觉就是,这货非常适合做后台管理界面,且基于jquery,很容易上手.当然, ...
- JAVA EXAM3 复习提纲
[Practice11_Zipcode_ArrayList] Zipcode class: //3 variables: zipcode, city, county, and compare by c ...
- c#、.net、asp.net、asp 、ado.net、.net framework的区别
c#:一种编程语言 .net:一种运行环境 asp.net:基于.netFramework框架下的一种开发技术(相对与asp而言,引入了服务器控件,前后台可分,编译型的编程框架) asp:也是.net ...
- 处理后台向前台传递的json数据
在pom文件中添加下面三种依赖jar包 <dependency> <groupId>com.fasterxml.jackson.core</groupId> < ...