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. Flex Basis与Width的区别

    [Flex Basis与Width的区别] Flex Items的应用准则 content –> width –> flex-basis (limted by max|min-width) ...

  2. django models返回数据根据某字段倒序排列

    例如有一个models表叫做report,report表中有一个endtime,想将结果按照endtime倒序排列   正序排列的方法:[models对象.objects.order_by(“字段名& ...

  3. myeclipse2014安装aptana3.4.0插件(转)

    1.下载aptana3.4.0_eclipse的zip包  http://pan.baidu.com/s/1qXOiZl6 或者是:https://pan.baidu.com/s/1jIqOYcI 2 ...

  4. VM虚拟机的配置文件(.vmx)损坏

    为了禁用时间同步,使用sublime修改vmx文件 文件第一行为.encoding = "GBK" 修改完毕,无法打开虚拟机,报 VM虚拟机的配置文件(.vmx)损坏错误 因为su ...

  5. STL::array

    1,array(仅c++11支持) 固定大小的容器,不能进行扩展和缩小(vector 可以),预分配的大小只是一个参数,在编译时确定真正的大小. Iterator 有下面几种: begin: [ ) ...

  6. python之列表、元组、字典学习

    list特征列表中的元素可以是数字和字符串,列表,布尔值,列表中也可以嵌套列表 a=[1,2,3,"qqq","无"] b=[1,2,3,[1,2,3,&quo ...

  7. centos 6.5 DNS服务器 搭建

    一.DNS 介绍 DNS(Domain Name System,域名系统),因特网上作为域名和IP地址相互映射的一个分布式数据库,DNS协议运行在UDP协议之上,使用端口号53(Domain), 95 ...

  8. Shc 应用

    1.说明 shc是一个加密shell脚本的工具, 它的作用是把shell脚本转换为一个可执行的二进制文件 2.安装 下载 # mget  http://www.datsi.fi.upm.es/~fro ...

  9. pandas_1

    大熊猫10分钟 这是对熊猫的简短介绍,主要面向新用户.您可以在Cookbook中看到更复杂的食谱. 通常,我们导入如下: In [1]: import numpy as np In [2]: impo ...

  10. Wasserstein距离

    https://blog.csdn.net/leviopku/article/details/81388306 https://blog.csdn.net/nockinonheavensdoor/ar ...