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. love is ... ...

    16 years old, love is dream.20 years old, love is sex.30 years old, love is marriage. 40 years old, ...

  2. python 任务计划

    sched    模块  引用time类实现任务定时执行 import time import sched def worker(msg): print msg s = sched.scheduler ...

  3. java面试题:Spring

    Spring 面试时,最好能结合底层代码说出IOC,AOP或Spring MVC的流程,能说出拦截器的底层. 如果看过Spring的源码,并能结合设计模式表达,是很大的加分项. IOC Q:讲一下IO ...

  4. Linux下常用的编辑文件与保存命令

    打开文件: vi aaa.conf 编辑: i 编辑结束,按ESC 键 跳到命令模式,然后输入退出命令: :w (write)保存文件但不退出vi 编辑 :w! 强制保存,不退出vi 编辑 :w fi ...

  5. verilog之inout

    1.inout 类型的data信号 写操作有效时(rd_wr_l=0):data端口输入信号,此时data为高阻态,允许对其进行赋值. 读操作有效时(rd_wr_l=1):data端口输出信号,此时d ...

  6. APIView和View的区别

    APIView和View的区别 API继承了View 重写了as_view方法 --豁免csrf def dispatch(self, request, *args, **kwargs): self. ...

  7. Unity3D研究院之设置自动旋转屏幕默认旋转方向

    如下图所示,在处理屏幕默认旋转方向的时候可以在这里进行选择,上下左右一共是4个方向. 策划的需求是游戏采用横屏,但是要求支持两个方向自动旋转,如下图所示,我的设置是这样的. Default Orien ...

  8. Wannafly挑战赛14 C.可达性(tarjan缩点)

    题目描述 给出一个 0 ≤ N ≤ 105 点数.0 ≤ M ≤ 105 边数的有向图, 输出一个尽可能小的点集,使得从这些点出发能够到达任意一点,如果有多个这样的集合,输出这些集合升序排序后字典序最 ...

  9. 【C】C语言中的_exit()与exit()

    _exit()和exit()主要区别是一个退出进程会清理I/O缓冲区,一个直接结束进程进入到内核中. 举例说明: #include <stdio.h> /*demo01 程序只输出 hel ...

  10. MyBatis入门程序(1)

    一.入门程序: 1.mybatis的配置文件SqlMapConfig.xml 配置mybatis的运行环境,数据源.事务等. <?xml version="1.0" enco ...