446是413. Arithmetic Slices问题同思路的DP多维扩展版。
代码与说明
先给出413代码:
public int numberOfArithmeticSlices(int[] A) {
int res=0;
int dp=0;
if(A.length<3)return res;
int diff=A[1]-A[0]; // 默认搜索时动态地维护一个差值,不满足该差值时重启计算
for(int i=2;i<A.length;i++){
if(A[i]-A[i-1]==diff){
dp=dp+1; //差值得到满足,数列持续扩充
}else{
diff=A[i]-A[i-1]; //跳出当前差值数列
dp=0;
}
res+=dp;
}
return res;
}
dp
代表的是在当前位置新增的等差数列个数;
当前满足条件A[i]-A[i-1]==diff
时,代表可以在当前维护的等差数列后添加元素A[i]
,规则如下:
数组总长度\等差数列长度 | 3 | 4 | 5 |
---|---|---|---|
3 | 1 | 0 | 0 |
4 | 2 | 1 | 0 |
5 | 3 | 2 | 1 |
即dp[i]=dp[i-1]+1
,亦即dp=dp+1
;
由于是将A[i]-A[i-1]
与A[i-1]-A[i-2]
做比较,永远满足数列长度>=3,dp
更新后即能累加到res
上。
该问题只考虑了到达某一位置时有唯一的diff,而446题需要到达某一位置i
后,考虑0<=j<i
上,所有可能的diff
。
446参考高赞代码:
public int numberOfArithmeticSlices(int[] A) {
Map<Integer,Integer>[]map=new Map[A.length];
int res=0;
for(int i=0;i<A.length;i++){
map[i]=new HashMap<>();
for(int j=0;j<i;j++){
long diff=(long)A[i]-A[j];
if(diff>=Integer.MAX_VALUE||diff<=Integer.MIN_VALUE){
continue;
}
int d=(int)diff;
int c1=map[i].getOrDefault(d,0);
int c2=map[j].getOrDefault(d,0);
res+=c2;
map[i].put(d,c1+c2+1);
}
}
return res;
}
算法理解
(代码由后往前解释更容易)
到达位置i
后,向前找到位置j
,此时有想要寻找的等差数列差值diff=A[i]-A[j]
:
map[i].put(d,c1+c2+1)
更新的是:由位置i
作为数列末尾元素确定的差值为diff
的等差数列,将来再遇到一个能被扩充到末尾的元素时,最终结果会增加的数列个数(预测值);由两部分组成:之前已预测的c1
和本次新预测的c2+1
;将会作为c1
或c2
在以后参与计算。
int c2=map[j].getOrDefault(d,0)
得到的是j
位置上预测的如果A[i]
可以扩充等差数列,则最终结果会增加的数列个数(道理同上表解释dp=dp+1
);c2=0
代表A[i]
与A[j]
形成了新的长度为2的预备等差数列,下一次再遇到可扩充元素,等效为在总结果上+1;c2!=0
代表A[i]
可以扩充到由位置j
作为数列末尾元素确定的差值为diff
的等差数列,且造成总结果res+=c2
。
int c1=map[i].getOrDefault(d,0)
得到的是该i
之前由其他j
确定的预测值,在本次j
循环找新的预测值后,需要将其累加上去。
例如[2,2,4,6]
,i=2
时c1=map[2].get(2)
先在j=0
时等于0,接着在j=1
时等于1,更新位置i=2
的预测值时,以元素4为结尾,diff=2
的预备等差数列有两个。