题干
原题网址:
https://leetcode.com/problems/arithmetic-slices/description/
题干解析
给你一个数列,要你算出他有多少个等差子序列(等差子序列的长度应该大于等于3)。
知识点
动态规划
难度
中等
解题思路
本题可用动态规划求解,思路有点类似于动态规划的经典题目之求回文子串个数(注意,因为本题要求的是连续的,所以是子串不是子序列),如果熟悉求回文子串的话,修改一下就可以解本道题了。
首先用指针进行动态申请内存,生成一个二维的标志子序列是否为等差的表,行号表示这个子序列的第一个数字在原vector里面的下标,而“列号+i”表示子序列最后一个数字在原vector里面的值。注意这里,我不是直接用列号表示子序列最后一个数字的下标,而是用列号+i,比如:第i行的列号为0的数字,代表着以第i个数字为起始,以第0+i个,也就是第i个数字为截止的长度为1的子序列;第i行的列号为1的数字,代表着以第i个数字为起始,以第i+1个数字为截止的长度为2的子序列。之所以这样做,是因为要节省空间。倘若我直接用列号表示子序列最后一个数字在原序列中的下标的话,我们知道最后一个数字的下标肯定大于等于第一个数字下标,那么那些列号小于行号的就相当于浪费掉了,浪费了将近一半的空间,而且博主亲测这样交上去的话会超出题目的内存限制=。=通过遍历,计算出各个子序列是否为等差,在写代码时这里要注意避免重复计算,其他的就没有什么太大的问题。具体代码如下。
代码
int** is_or_not; // 标志数列是否为等差的
class Solution {
public:
void is_slices(vector<int>& A, int i, int j) { // 计算A中的第i个到第j+i个数组成的数列是否为等差序列
if (j < 2) { // 如果长度小于3,使其为真
is_or_not[i][j] = 1;
return;
}
if (is_or_not[i][j] != -1) { // 如果之前已经计算过了,不再重复计算
return;
} else {
if (is_or_not[i + 1][j - 1] == -1) { // 如果该序列去掉头尾数字的子序列没有计算过,先计算一下
is_slices(A, i + 1, j - 1);
}
if (is_or_not[i + 1][j - 1] == 0) { // 如果他的子序列不是等差的,那么他也不是等差的
is_or_not[i][j] = 0;
} else if (is_or_not[i + 1][j - 1] == 1) { //如果他的子序列是等差的
if ((A[i] - A[i + 1]) == (A[i + 1] - A[i + 2]) && (A[j + i] - A[j + i - 1]) == (A[j + i - 1] - A[j + i - 2])) { //如果他的头尾数字分别与其内部邻居的差值,等于他的等差子序列公差,则他也是等差的
is_or_not[i][j] = 1;
} else {
is_or_not[i][j] = 0;
}
}
return;
}
}
int numberOfArithmeticSlices(vector<int>& A) {
// 动态申请内存
is_or_not = new int*[A.size()];
for (int i = 0; i < A.size(); i++) {
is_or_not[i] = new int[A.size() - i]; // 因为如果这里长度也为A.size()的话会超出内存限制,所以进行了小处理(减少了内存)
for (int j = 0; j < A.size() - i; j++) {
is_or_not[i][j] = -1; // 初始化
}
}
//遍历计算各个子数列是否为等差的
for (int i = 0; i <A.size(); i++) {
for (int j = 0; j < A.size() - i; j++) {
is_slices(A, i, j);
}
}
// 算出等差子序列的个数
int ans = 0;
for (int i = 0; i < A.size(); i++) {
for (int j = 0; j < A.size() - i; j++) {
if (is_or_not[i][j] == 1 && j >= 2) {
ans++;
}
}
}
// 释放动态申请的内存
for (int i = 0; i < A.size(); i++) {
delete is_or_not[i];
}
delete is_or_not;
return ans; // 返回答案
}
};