题目:

给定一整数数列,问数列有多少个子序列是等差数列。

即对于包含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】等差子序列个数的更多相关文章

  1. BZOJ 2124: 等差子序列

    Sol 线段树+Hash. 首先暴力 等差子序列至少3项就可以了,就枚举中项,枚举公差就可以了,只需要一个数在中项前出现,另一个数在中项前没出现过就可以了.复杂度 \(O(n^2)\) 然后我想了一个 ...

  2. [bzoj2124]等差子序列(hash+树状数组)

    我又来更博啦     2124: 等差子序列 Time Limit: 3 Sec  Memory Limit: 259 MBSubmit: 941  Solved: 348[Submit][Statu ...

  3. codevs 1283 等差子序列

    http://codevs.cn/problem/1283/ 题目描述 Description 给一个 1 到 N 的排列{Ai},询问是否存在 1<=p1<p2<p3<p4& ...

  4. P2757 [国家集训队]等差子序列

    P2757 [国家集训队]等差子序列 题目传送门 推荐一篇好题解 此题要求我们在一个序列中找出一个等差子序列. 显然,我们只需要考虑子序列长度len=3的情况,因为在长度为4的子序列中必定有一个长度为 ...

  5. [BZOJ2124]等差子序列/[CF452F]Permutation

    [BZOJ2124]等差子序列/[CF452F]Permutation 题目大意: 一个\(1\sim n\)的排列\(A_{1\sim n}\),询问是否存在\(i,j(i<j)\),使得\( ...

  6. bzoj 2124 等差子序列 (线段树维护hash)

    2124: 等差子序列 Time Limit: 3 Sec  Memory Limit: 259 MBSubmit: 1922  Solved: 714[Submit][Status][Discuss ...

  7. bzoj 2124 等差子序列 树状数组维护hash+回文串

    等差子序列 Time Limit: 3 Sec  Memory Limit: 259 MBSubmit: 1919  Solved: 713[Submit][Status][Discuss] Desc ...

  8. 【bzoj2124】等差子序列 STL-bitset

    题目描述 给一个1到N的排列{Ai},询问是否存在1<=p1<p2<p3<p4<p5<…<pLen<=N (Len>=3),使得Ap1,Ap2,A ...

  9. 等差子序列(sequence)

    等差子序列(sequence) 题目描述 给一个1到N的排列{Ai},询问是否存在1<= p1 < p2 < p3 < p4 < p5 < - < pLen ...

随机推荐

  1. burp 代理的时候无法访问https网站

    今天在使用burp的时候发现不能访问https网站了,Google下面还出现这个 ERR_SSL_VERSION_OR_CIPHER_MISMATCH,于是到官网下载了一个最新的burp就可以访问了, ...

  2. task19-21

    [说明]理想是丰满的,现实很骨感,昨天还说今天有望干掉5个小任务,看来是没可能了,兜兜转转地做了一天也才完成下面的这些 一:今日完成 19.学习Spring,配置Spring和Junit 1)先安装一 ...

  3. 【问题解决】Tomcat 启动时闪退或提示“Neither the JAVA_HOME or the JRE_HOME environmental variable is defined.”

    问题解决思路: 1.分析startup.bat启动脚本:发现其调用了catalina.bat,而catalina.bat调用了setclasspath.bat 2.在setclasspath.bat的 ...

  4. 【BZOJ5037】[Jsoi2014]电信网络 最大权闭合图

    [BZOJ5037][Jsoi2014]电信网络 Description JYY创建的电信公司,垄断着整个JSOI王国的电信网络.JYY在JSOI王国里建造了很多的通信基站.目前所有的基站都是使用2G ...

  5. 【python】-- RabbitMQ RPC模型

    RabbitMQ RPC模型 RPC(remote procedure call)模型说通俗一点就是客户端发一个请求给远程服务端,让它去执行,然后服务端端再把执行的结果再返回给客户端. 1.服务端 i ...

  6. [DBNETLIB][ConnectionOpen(Connect()).]SQL Server 不存在或拒绝访问 数据库错误 解决办法总结

    连接数据库报错:“数据库异常:[DBNETLIB] [ConnectionOpen(Connenct()).] Sqlserver 不存在或拒绝访问” 原因: 1.查看是不是没有在数据库中添加数据库服 ...

  7. F5与Ctrl+F5及地址栏输入地址回车

    按F5等同于点击页面地址栏的刷新图标. 地址栏输入地址然后回车: 根据缓存内容是否过期决定是否发送请求给服务端 F5: 浏览器无论如何都得发送请求给服务端,包含If-Modified-Since/If ...

  8. 一看就懂的数据库范式介绍(1NF,2NF,3NF,BC NF,4NF,5NF)

    原文:http://josh-persistence.iteye.com/blog/2200644 一.基本介绍 设计关系数据库时,遵从不同的规范要求,设计出合理的关系型数据库,这些不同的规范要求被称 ...

  9. ThinkPHP 小技巧

    中文截取函数 函数解释: msubstr($str, $start=0, $length, $charset=”utf-8″, $suffix=true) $str:要截取的字符串 $start=0: ...

  10. electron—Chromium有酒,Node有肉

    谷歌V8引擎的出现,Node.js的诞生注定要把开发模式“搅乱”. 基于云应用,服务化,定制化的应用需求不断增加后使得传统的winform开发空间越来越小,而原来做前端的空间越来越大,Node.js ...