作者推荐
本文涉及知识点
446. 等差数列划分 II - 子序列
给你一个整数数组 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 <= nums.length <= 1000
-231 <= nums[i] <= 231 - 1
动态规划
时间复杂度😮(nn)
空间复杂度😮(nn)
变量解析
长度大于2的称为等差子序列,长度等于2的不妨称为“准等差”。
mSubCount1 | mSubCount1[i][sub]表示以nums[i]结尾,差为sub的“准等差”数量。 |
mSubCount2 | mSubCount2[i][sub]表示以nums[i]结尾,差为sub的等差数列的数量。 |
动态规划的细节,方便检查
两层循环,分别枚举等差数列的最后一个元素和倒数第二个元素。
动态规划的状态表示 | mSubCount1 和mSubCount2 |
动态规划的转移方程 | mSubCount2 [i][sub] +=mSubCount1 [j][sub]+ mSubCount2 [j][sub] mSubCount1[i][sub]++ |
动态规划的初始状态 | 空 |
动态规划的填表顺序 | i和j都是从小到大处理,确保动态规划的无后效性 |
动态规划的返回值 | Sumi,submSubCount2[i][sub] |
代码
核心代码
class Solution { public: int numberOfArithmeticSlices(vector<int>& nums) { m_c = nums.size(); vector<unordered_map<long long, int>> mSubCount1(m_c), mSubCount2(m_c); int iRet = 0; for (int i = 1; i < m_c; i++) { for (int j = 0; j < i; j++) { const long long sub = (long long)nums[i] - nums[j]; if (mSubCount2[j].count(sub)) { mSubCount2[i][sub] += mSubCount2[j][sub]; } if (mSubCount1[j].count(sub)) { mSubCount2[i][sub] += mSubCount1[j][sub]; } mSubCount1[i][sub]++; } for (const auto& [_tmp,cnt] : mSubCount2[i]) { iRet += cnt; } } return iRet; } int m_c; };
测试用例
template<class T> void Assert(const T& t1, const T& t2) { assert(t1 == t2); } template<class T> void Assert(const vector<T>& v1, const vector<T>& v2) { if (v1.size() != v2.size()) { assert(false); return; } for (int i = 0; i < v1.size(); i++) { Assert(v1[i], v2[i]); } } int main() { vector<int> nums; { Solution sln; nums = { 1,1,1,1 }; auto res = sln.numberOfArithmeticSlices(nums); Assert(5, res); } { Solution sln; nums = { 2, 4, 6, 8, 10 }; auto res = sln.numberOfArithmeticSlices(nums); Assert(7, res); } { Solution sln; nums = { 7,7,7,7,7 }; auto res = sln.numberOfArithmeticSlices(nums); Assert(16, res); } { Solution sln; nums = { 0, 2000000000, -294967296 }; auto res = sln.numberOfArithmeticSlices(nums); Assert(16, res); } }
可以优化掉一个变量
class Solution {
public:
int numberOfArithmeticSlices(vector& nums) {
m_c = nums.size();
vector<unordered_map<long long, int>> mSubCount(m_c);
int iRet = 0;
for (int i = 1; i < m_c; i++)
{
for (int j = 0; j < i; j++)
{
const long long sub = (long long)nums[i] - nums[j];
if (mSubCount[j].count(sub))
{
mSubCount[i][sub] += mSubCount[j][sub];
iRet += mSubCount[j][sub];
}
mSubCount[i][sub]++;
}
}
return iRet;
}
int m_c;
};
2023年1月版
class Solution {
public:
int numberOfArithmeticSlices(vector& nums) {
m_c = nums.size();
vector<std::unordered_map<long long, int>> dp(m_c);
int iRet = 0;
for (int i = 0; i < m_c; i++)
{
for (int j = 0; j < i; j++)
{
long long tmp = 1LL * nums[i] - nums[j];
auto it = dp[j].find(tmp);
int iNum = (dp[j].end() == it) ? 0 : it->second ;
iRet += iNum;
dp[i][tmp] += iNum + 1;
}
}
return iRet;
}
int m_c;
};