446. Arithmetic Slices II - Subsequence
Hard

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的更多相关文章

  1. [LeetCode] Arithmetic Slices II - Subsequence 算数切片之二 - 子序列

    A sequence of numbers is called arithmetic if it consists of at least three elements and if the diff ...

  2. LeetCode 446. Arithmetic Slices II - Subsequence

    原题链接在这里:https://leetcode.com/problems/arithmetic-slices-ii-subsequence/ 题目: A sequence of numbers is ...

  3. Leetcode: Arithmetic Slices II - Subsequence

    A sequence of numbers is called arithmetic if it consists of at least three elements and if the diff ...

  4. [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 ...

  5. LeetCode446. Arithmetic Slices II - Subsequence

    A sequence of numbers is called arithmetic if it consists of at least three elements and if the diff ...

  6. 446. Arithmetic Slices II - Subsequence

    A sequence of numbers is called arithmetic if it consists of at least three elements and if the diff ...

  7. 446 Arithmetic Slices II - Subsequence 算数切片之二 - 子序列

    详见:https://leetcode.com/problems/arithmetic-slices-ii-subsequence/description/ C++: class Solution { ...

  8. 第六周 Leetcode 446. Arithmetic Slices II - Subsequence (HARD)

    Leetcode443 题意:给一个长度1000内的整数数列,求有多少个等差的子数列. 如 [2,4,6,8,10]有7个等差子数列. 想了一个O(n^2logn)的DP算法 DP[i][j]为 对于 ...

  9. [LeetCode] Arithmetic Slices 算数切片

    A sequence of number is called arithmetic if it consists of at least three elements and if the diffe ...

随机推荐

  1. JavaWeb网站后台开发记录手册

    1.javaweb网站后台开发 1.封装DBTools类 1.注册数据库驱动 Class.forName("oracle.jdbc.driver.OracleDriver"); 2 ...

  2. leecode 937 Reorder Log Files (模拟)

    传送门:点我 You have an array of logs.  Each log is a space delimited string of words. For each log, the ...

  3. 导入Unity插件时出现Failed to import package with error: Couldn't decompress package

    导入Unity插件时出现Failed to import package with error: Couldn't decompress package 一开始以为压缩包本身有问题,坏了 后来发现在父 ...

  4. exercise 1-6

    [买菜] package Practice06; import java.io.File; import java.io.FileNotFoundException; import java.util ...

  5. spring boot 2.0 与FASTDFS进行整合

    只支持在spring-boot 2.0以及以上版本中使用 1.pom.xml 里引入FASTDFS的依赖,toobato与fastdfs作者一起,将fastdfs的功能进行了重构与简化 <!-- ...

  6. JAVA之1000字感想

    在经过Java自学的测试之后,我发现了自己所暴露的问题. 第一,   对于没有解决的问题始终没有解决,比如对文件的导入导出,在假期自学的时候就没有弄懂,现在依然没有解决,而现在没有解决,以后对于数据库 ...

  7. Linux下tar.gz、tar、bz2、zip等解压缩、压缩命令小结(转)

    本文介绍了linux下的压缩程式tar.gzip.gunzip.bzip2.bunzip2.compress .uncompress. zip. unzip.rar.unrar等程式,以及如何使用它们 ...

  8. git查看历史操作

    在提交了若干更新,又或者克隆了某个项目之后,偶尔想回顾下过往提交历史.可以使用git log命令来实现. 最简单的查看提交历史命令如下: $ git log $ git log --oneline $ ...

  9. error:cv_bridge---opencv和ros连接起来的桥

    有多个warning出现...版本冲突 解决办法: cv_bridge[opencv和ros连接起来的桥],默认依赖的opencv版本是2.4.8,如果安装了新的opencv版本,比如3.1.0,那么 ...

  10. 将文件转换成byte[]数组

    代码 /// <summary> /// 将文件转换成byte[] 数组 /// </summary> /// <param name="fileUrl&quo ...