【Facebook】等差子序列个数
题目:
给定一整数数列,问数列有多少个子序列是等差数列。
即对于包含N个数的数列A,A(0),A(1),……,A(N-1),有多少组(P(0),P(1),……,P(k))满足0<=P(0)<P(1)<……<P(k)<N,且A(P(0)),A(P(1)),……,A(P(k))为等差数列。
等差数列至少包含3个数,故必有k>=2,同时等差数列相邻两个数的差都是一样的,即A(P(1))-A(P(0) = A(P(2))-A(P(1)) = …… = A(P(k))-A(P(k-1)) = d,d被称为公差。
输入保证N个整数的取值范围均为-2^31 ~ 2^31-1,并且0<=N<=1000,同时保证输出小于2^31-1。
Example:
输入: [2, 4, 6, 8, 10] 输出: 7
题解:
来源:九章算法公众号(侵删)。
时间复杂度为O(N^2)的动态规划:
Ⅰ.我们令f(i,d)表示以A(i)结尾,公差为d的等差子序列的个数,这里我们允许存在长度为2的等差子序列(所以对于数列中任意两个数组成的子序列,我们都暂时认为其为等差子序列)。
那么对于一对(i,j),j<i,A(i)-A(j)=d,对于所有以A(j)结尾,公差为d的等差子序列来说,后面再跟上A(i)之后还是公差为d的等差子序列,但变成了以A(i)结尾,再加上一对(A(j),A(i)),就得到了所有形如(……,A(j),A(i))的等差子序列。
换言之,j将对f(i,d)贡献f(j,d)+1。故f(i,d)等于所有满足j<i且A(i)-A(j)=d的(f(j,d)+1)之和。
Ⅱ.一个问题是d的范围其实很大(-2^32+1 ~ 2^32-1),如果要对所有可能的d进行枚举,那么在时间上和空间上都是受不了的。
虽然d的取值范围很大,但是对于N个数来说,两两之差最多只可能有N(N-1)/2种;而对于1个数A(i)来说,只需考虑所有小于i的j所产生的d=A(i)-A(j),最多有i种可能。
所以,对于每一个i,可以用一个HashMap来存储键值对(d,f(i,d))。另一个问题是,我们在计算f(i,d)时,允许等差子序列长度为2(这一点是必要的,因为没有长度为2的序列的话,就没法在其末尾加上一个数得到更长的子序列),但答案要求的是所有长度至少为3的等差子序列的个数。
解决这个问题的方法有很多:在计算f(i,d)时,f(j,d)所表示的所有子序列长度都至少为2,在末尾加上A(i)之后,就成了满足条件的等差子序列,故可以在计算f(i,d)的同时累加所有f(j,d),最后即可得到正确的答案(这种写法比较简洁但不太直观);
也有一种比较容易理解的方法,那就是对所有f(i,d)之和,即所有长度至少为2的等差子序列的个数,减去长度为2的等差子序列的个数,而由于任意两个数都构成长为2的等差子序列,所以其个数为N(N-1)/2,两者相减得到的差即为正确答案。
Ⅲ.总结一下这个动态规划算法:对于每个i=0,1,2,……,N-1,创建一个HashMap存储键值对(d,f(i,d)),f(i,d)的初值为0,枚举j<i,d=A(i)-A(j),则f(i,d)增加f(j,d)+1,同时对答案增加f(j,d)。计算完所有的i之后即可得到答案。
一个小细节是,如果d不在[-2^31+1 , 2^31-1]的范围内,那么以这个d为公差的数列长度不可能是3或3以上,故对于d在这个范围外的情况可以直接跳过。
利用HashMap存取f(i,d),f(j,d)的复杂度为O(1),i,j枚举的复杂度为O(N^2),故总的时间复杂度为O(N^2)。
Solution 1 :
int getNum(const vector<int> &nums) {
if (nums.size() < ) {
return ;
}
vector<unordered_map<int, int>> map(nums.size());
int res = ;
for (int i = ; i < nums.size(); ++i){
for (int j = ; j < i; ++j) {
if (abs((long)nums[i] - nums[j]) > INT_MAX) {
continue;
}
int d = nums[i] - nums[j];
int map_i_d, map_j_d;
map_i_d = map[i].count(d) ? map[i][d] : ;
map_j_d = map[j].count(d) ? map[j][d] : ;
map_i_d += map_j_d + ;
map[i][d] = map_i_d;
res += map_j_d;
}
}
return res;
}
事实上,确定一个等差数列只需要三个数,一个是等差数列的长度L,还有两个是等差数列的最后两个数(也可以是任意两个中间的下标确定的数)。
记最后一个为E1,最后第二个为E2,则得公差d=E1-E2,通过公差可以推出等差数列中其余的数。
一个以E2,E1,结尾的等差数列,在末尾加上一个数E1+d后仍然是等差数列。于是我们可以使用动态规划求解:令g(i,j)为以A(j),A(i)结尾的等差子序列的个数(j<i),(即形如(……,A(j),A(i))的等差数列的个数),然后我们可以通过枚举倒数第三个数A(k)来统计g(i,j)。
对于形如(……,A(k),A(j))的等差子序列来说,如果有A(i)-A(j)=A(j)-A(k),那么对应的(……,A(k),A(j),A(i))也为等差子序列,同时由于(A(k),A(j))长度为2,不计入g(j,k)的中,但(A(k),A(j),A(i))应计入g(i,j)中,故将g(j,k)计算入g(i,j)时还要额外加1。
于是我们有g(i,j)=Σ(g(j,k)+1),其中k满足k<j且A(i)-A(j)=A(j)-A(k)。将所有得到的g(i,j)相加即可得到所有等差子序列的个数。这个算法的时间复杂度为O(N^3),考虑到N的范围,这样的时间复杂度可以接受,而且与上面讲的算法相比简洁许多。
Solution 2 :
int getAns(const vector<int> &nums) {
if (nums.size() < )
return ;
int n = nums.size();
vector<vector<int>> v(n, vector<int>(n, ));
int res = ;
for (int i = ; i < n; ++i) {
for (int j = ; j < i; ++j) {
for (int k = ; k < j; ++k) {
if (nums[i] - nums[j] == nums[j] - nums[k]) {
v[i][j] = v[j][k] + ;
res += v[i][j];
}
}
}
}
return res;
}
【Facebook】等差子序列个数的更多相关文章
- BZOJ 2124: 等差子序列
Sol 线段树+Hash. 首先暴力 等差子序列至少3项就可以了,就枚举中项,枚举公差就可以了,只需要一个数在中项前出现,另一个数在中项前没出现过就可以了.复杂度 \(O(n^2)\) 然后我想了一个 ...
- [bzoj2124]等差子序列(hash+树状数组)
我又来更博啦 2124: 等差子序列 Time Limit: 3 Sec Memory Limit: 259 MBSubmit: 941 Solved: 348[Submit][Statu ...
- codevs 1283 等差子序列
http://codevs.cn/problem/1283/ 题目描述 Description 给一个 1 到 N 的排列{Ai},询问是否存在 1<=p1<p2<p3<p4& ...
- P2757 [国家集训队]等差子序列
P2757 [国家集训队]等差子序列 题目传送门 推荐一篇好题解 此题要求我们在一个序列中找出一个等差子序列. 显然,我们只需要考虑子序列长度len=3的情况,因为在长度为4的子序列中必定有一个长度为 ...
- [BZOJ2124]等差子序列/[CF452F]Permutation
[BZOJ2124]等差子序列/[CF452F]Permutation 题目大意: 一个\(1\sim n\)的排列\(A_{1\sim n}\),询问是否存在\(i,j(i<j)\),使得\( ...
- bzoj 2124 等差子序列 (线段树维护hash)
2124: 等差子序列 Time Limit: 3 Sec Memory Limit: 259 MBSubmit: 1922 Solved: 714[Submit][Status][Discuss ...
- bzoj 2124 等差子序列 树状数组维护hash+回文串
等差子序列 Time Limit: 3 Sec Memory Limit: 259 MBSubmit: 1919 Solved: 713[Submit][Status][Discuss] Desc ...
- 【bzoj2124】等差子序列 STL-bitset
题目描述 给一个1到N的排列{Ai},询问是否存在1<=p1<p2<p3<p4<p5<…<pLen<=N (Len>=3),使得Ap1,Ap2,A ...
- 等差子序列(sequence)
等差子序列(sequence) 题目描述 给一个1到N的排列{Ai},询问是否存在1<= p1 < p2 < p3 < p4 < p5 < - < pLen ...
随机推荐
- webview300毫秒点击问题
http://www.jshacker.com/note/3585 http://blog.csdn.net/zfy865628361/article/details/49512095 http:// ...
- Xshell连接不上行Linux系统
之前出现了.我的Linux系统上的主机可以ping通windows的一台电脑, windows也可以ping的通Linux.但是Xshell不能连接. 第一,我感觉是不是我Linux系统的端口被防火墙 ...
- Java基础 - 变量的定义和使用
变量定义 public class Main { public static void main(String[] args) { // 定义byte类型的变量 byte b = 10; System ...
- [原创]java WEB学习笔记40:简单标签概述(背景,使用一个标签,标签库的API,SimpleTag接口,创建一个自定义的标签的步骤 和简单实践)
本博客为原创:综合 尚硅谷(http://www.atguigu.com)的系统教程(深表感谢)和 网络上的现有资源(博客,文档,图书等),资源的出处我会标明 本博客的目的:①总结自己的学习过程,相当 ...
- 第一篇 css导入方式 及选择器
一 推荐资料 推荐书籍 css Zen Garden 中文(css禅意花园) 二.css样式 1.css样式表特征 继承性 大多数css的样式规则可以被继承 层叠性 1)可以定义 多个样式 2)不冲 ...
- build-essential软件包
linux操作系统上面开发程序, 光有了gcc 是不行的 它还需要一个 build-essential软件包 作用是提供编译程序必须软件包的列表信息 也就是说 编译程序有了这个软件包 它才知道 头文件 ...
- 什么是DDOS攻击?怎么防御?
一.什么是DDOS? DDOS是英文Distributed Denial of Service的缩写,意即"分布式拒绝服务",那么什么又是拒绝服务(Denial of Servic ...
- HDU 4267 A Simple Problem with Integers(2012年长春网络赛A 多颗线段树+单点查询)
以前似乎做过类似的不过当时完全不会.现在看到就有点思路了,开始还有洋洋得意得觉得自己有不小的进步了,结果思路错了...改了很久后测试数据过了还果断爆空间... 给你一串数字A,然后是两种操作: &qu ...
- 关于spring事务注解
关于事务的注解常用如下1.如果有事务, 那么加入事务, 没有的话新建一个(默认情况下),也就是当我们要开启事务的时候才用,即有数据库有增删改操作@Transactional(rollbackFor=E ...
- codeforces 460D:Little Victor and Set
Description Little Victor adores the sets theory. Let us remind you that a set is a group of numbers ...