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. web 浏览器窗口

    1.窗口位置: 取得浏览器窗口左边和上边距屏幕左边和上边的位置 var leftPos = (typeof window.screenLeft == "number")? wind ...

  2. 安装Eclipse Maven插件的几种方法

    文章出处:http://blog.csdn.net/lfsfxy9/article/details/9397937 感谢作者的分享! 昨天直接在机器上配置了Maven环境,今天顺便把Eclipse等I ...

  3. 如何用 Postman 处理 json请求格式

    下边是其他博友写的 http://blog.163.com/huan12_8/blog/static/130519090201611711213719/

  4. php苹果内购订单验证

    /** * 21000 App Store不能读取你提供的JSON对象 * 21002 receipt-data域的数据有问题 * 21003 receipt无法通过验证 * 21004 提供的sha ...

  5. 二分+最短路 UVALive - 4223

    题目链接:https://vjudge.net/contest/244167#problem/E 这题做了好久都还是超时,看了博客才发现可以用二分+最短路(dijkstra和spfa都可以),也可以用 ...

  6. 测试--错误java.lang.Exception: No tests found matching [{ExactMatcher:fDisplayName=select], {ExactMatcher:fDisplayName=select(com.rjj.demo.DemoApplicationTests)]...

    异常这个错误java.lang.Exception: No tests found matching [{ExactMatcher:fDisplayName=select], {ExactMatche ...

  7. Mobile Game Development with Unity Build Once, Deploy Anywhere

    本书从自上而下的角度介绍了Unity游戏引擎的功能,并提供了具体的.面向项目的指导,说明了如何在真实的游戏场景中使用这些功能,以及如何从头开始构建让玩家爱不释手的2D和3D游戏.主要内容有:探索Uni ...

  8. 填坑专记-手脱FSG壳

      妈呀,脱FGS壳真的是坎坷颇多,多亏吾爱破解前辈们的帮忙.我一定要记录下来,省的以后再无法解决.   已经查看是FSG壳了.找到入口也容易了.重点就是脱壳并修复好它. 脱壳   OEP为:   使 ...

  9. Jmeter 录制脚本(二)

    1)选择WorkBench,右键 Add -> Non-Test Elements -> HTTP(S) Test Script Recorder 2)在HTTP(S) Test Scri ...

  10. vue router返回上一页

    this.$router.go(-1) 不用router时,使用window.history.go(-1);