昨天看了电视剧《我在他乡挺好的》大结局,三个小时,就没有时间写leetcode了,就当作休息啦
今天继续~
最近天天在公司写模型,但是今天好像收敛的没有那么理想,明天再研究一下。
注意和pcgg代码的兼容性呀
先是每日一题吧
居然是个困难题,看见难度级别就觉得没戏了,只争取部分用例通过吧。
446. 等差数列划分 II - 子序列
题目描述
https://leetcode-cn.com/problems/arithmetic-slices-ii-subsequence/
给你一个整数数组 nums ,返回 nums 中所有 等差子序列 的数目。
如果一个序列中 至少有三个元素 ,并且任意两个相邻元素之差相同,则称该序列为等差序列。
例如,[1, 3, 5, 7, 9]、[7, 7, 7, 7] 和 [3, -1, -5, -9] 都是等差序列。
再例如,[1, 1, 2, 5, 7] 不是等差序列。
数组中的子序列是从数组中删除一些元素(也可能不删除)得到的一个序列。
例如,[2,5,10] 是 [1,2,1,2,4,1,5,10] 的一个子序列。
题目数据保证答案是一个 32-bit 整数。
示例 1:
输入:nums = [2,4,6,8,10]
输出:7
解释:所有的等差子序列为:
[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]
示例 2:
输入:nums = [7,7,7,7,7]
输出:16
解释:数组中的任意子序列都是等差子序列。
提示:
1
<
=
n
u
m
s
.
l
e
n
g
t
h
<
=
1000
1 <= nums.length <= 1000
1<=nums.length<=1000
−
2
31
<
=
n
u
m
s
[
i
]
<
=
2
31
−
1
-2^{31} <= nums[i] <= 2^{31} - 1
−231<=nums[i]<=231−1
思路:
用时:23:33-
思路:
首先想到的是:
(1) 找到最长的等差子序列,然后每个部分也是等差
- 等差 > 0或 <0的时候
如示例1:[2,4,6,8,10]
长度为:len_max
这个玩意长度为5,差为2 = => 同样差为2的子序列有 长度为3 的 3个,长度为4 的 2个。
即: (1 + len_max - 2) * (len_max -2 )/2 ==> (len_max-1)(len_max-2) /2
– 为了避免重复,所以这种改进为:每次固定前两个,后面的长度随意长
所以这种是:len_max - 2
– 其他的可以
- 等差等于=0的时候
[7,7,7,7,7] 子序列的数量就是等差子序列的数量。
子序列的数量为 每个位选择要或者不要 $2^n $
-
-
长度<3的子序列数量
(
n
0
)
{n\choose 0}
(0n) +
(
n
1
)
{n\choose 1}
(1n) +
(
n
2
)
{n\choose 2}
(2n)
- 长度为0 的子序列数量 1
- 长度为1的子序列数量 n
-
长度为2的子序列数量 n(n-1)/2
= 2^n - (1 + n +n(n-1)/2 )
fun = lambda n:int(pow(2,n) - (1 + n + n*(n-1)/2 ))
(2)如何找到最长的等差子数列
看数组长度为1000,感觉复杂度
o
(
n
3
)
o(n^3)
o(n3) 已经是极限了。
算法:
len_nums = len(nums)
有一个大的map存储每个元素
num_dict:
其中:num_dict[num] = [] numindex的list,顺次加的
1- 指针1 :index1 从:0–>len_nums-3
指针2: index2 从 index1 -> len_nums-2
然后gap就确定了:
gap = nums[index2]-nums[index1]
new_num = nums[index2] + gap
2分查找最小的index,再找下一个元素。
直到找不到break。 记下这一组。
可能存在的问题:
- [2,4,4,6,8,10] --> 所有的都计算,会出现重复的。-- 那就固定前两个。固定前两个的
还有可能是个动态规划的题目,因为答案无后置性。
新增加一个数:
1- 原来的等差数列长度+1
1- 原来的等差数列长度不变,最后一个位换了
2- 构成新的等差数列
1000 * 1000 的矩阵也不是很大
这样的思路做的话
dp[i][j] = 以 nums[i] 结尾,上一个元素是nums[j]的 等差数列 的差和序列长度。
然后现在已经一点半了,我真的无比后悔花了两个小时想这个题。
–以后我再超过一个小时想题目,我就是猪。
直接抄答案不好么
尾项和公差可以确定一个等差数列,定义状态
f
[
i
]
[
d
]
f[i][d]
f[i][d] 表示尾项为
n
u
m
s
[
i
]
nums[i]
nums[i] ,公差为
d
d
d 的弱等差子序列的个数。
# 子序列基本都可以用二维动态数组
from typing import List
from collections import defaultdict
class Solution:
def numberOfArithmeticSlices(self, nums: List[int]) -> int:
res = 0
len_nums = len(nums)
if len_nums<3: return 0
dp = [[0 for i in range(len_nums)] for i in range(len_nums)]
# dp[i][j] 以nums[i] 结束,上一个元素为 nums[j] 的等差序列【长度为2】的种数
# 对角线为1
for i in range(len_nums):
if i >0:
dp[i][0] = 0.5 # 第一列都是2
num_dict = defaultdict(list)
for ind,num in enumerate(nums):
num_dict[num].append(ind)
# print(num_dict)
# 开始计算
for i in range(2,len_nums):
for j in range(1,i):
# print(dp)
gap = int(nums[i] - nums[j])
# 再上一个元素应该为:nums[j] -gap
last_ele = int(nums[j] -gap)
# print("last_ele:",last_ele)
if last_ele not in num_dict:
dp[i][j] = 0.5
continue
# 找到所有位置
last_ele_index = []
for last_ele_ind in num_dict[last_ele]:
if last_ele_ind < j:
last_ele_index.append(last_ele_ind)
if not last_ele_index:
dp[i][j] = 0.5
continue
tmp_ = 0
for last_ele_ind in last_ele_index:
if dp[j][last_ele_ind]>0.1 and dp[j][last_ele_ind] < 1:
tmp_ += 1
else:
tmp_ += dp[j][last_ele_ind]+1
dp[i][j] = tmp_
# 然后更新结果
res+= dp[i][j]
return res
终于,我还是做出来了,洗个澡睡觉啦,希望明天工作顺利~
– 主要需要思考的就是更新策略:
如果前面的长度为2,就直接累加,前面的长度已经大于=3了,就相当于新增了一个。
for last_ele_ind in last_ele_index:
if dp[j][last_ele_ind]>0.1 and dp[j][last_ele_ind] < 1:
tmp_ += 1
else:
tmp_ += dp[j][last_ele_ind]+1
晚安啦~