算法解析—同向双指针
字节笔试 万万没想到抓捕孔联顺,列表子元素最大间隔不超过D
最近在刷题时就看到这题,题目描述十分中二。
我叫王大锤,是一名特工。我刚刚接到任务:在字节跳动大街进行埋伏,抓捕恐怖分子孔连顺。和我一起行动的还有另外两名特工,我提议
- 我们在字节跳动大街的N个建筑中选定3个埋伏地点。
- 为了相互照应,我们决定相距最远的两名特工间的距离不超过D。
我特喵是个天才! 经过精密的计算,我们从X种可行的埋伏方案中选择了一种。这个方案万无一失,颤抖吧,孔连顺!
……
万万没想到,计划还是失败了,孔连顺化妆成小龙女,混在cosplay的队伍中逃出了字节跳动大街。只怪他的伪装太成功了,就是杨过本人来了也发现不了的!
请听题:给定N(可选作为埋伏点的建筑物数)、D(相距最远的两名特工间的距离的最大值)以及可选建筑的坐标,计算在这次行动中,大锤的小队有多少种埋伏选择。
注意:
- 两个特工不能埋伏在同一地点
- 三个特工是等价的:即同样的位置组合(A, B, C) 只算一种埋伏方法,不能因“特工之间互换位置”而重复使用
示例1
输入
4 3
1 2 3 4
输出
4
大致总结一下题意就是在N个building 中选择3个使得最远两个点的距离不超过D,求有多少这样的地点选择。
思路
首先这个题第一眼能想到组合法, 求出所有building中 3个的组合 N选3
这样的时间复杂度应该是阶乘级别。
然后仔细想一下可以简化思考一下,可以直接选择两头的点,只要两头的点小于D,那么,中间的所有 点都可以成为第三个点,因为是按照数轴顺序排列。
代码如下
import sys
first= sys.stdin.readline().strip()
n,d=list(map(int,first.split()))
line= sys.stdin.readline().strip().split()
b=list(map(int,line))
j=1
count=0
for i in range(n-1):
for j in range(i+2,n):
if b[j]-b[i]<=d:
count+=j-i-1
print(count%99997867)
0,1,2,3,4,5
^ ^
i, j —>
从i 的后一位开始,遍历到最后一位,如果两者距离小于等于D, 那么就加上中间所有的数,比如 0,4 就+3. 此方法时间复杂度O(n^2)
双指针法:
这种想法就需要些数学思维,想到如果两个点之间比如0,4 满足条件building[4]-building[0]<=D,那么1,4加上[1,4]中任意点也满足<=D
那么所有组合就是:
0 1 4,0 2 4, 0 3 4
1 2 4,1 3 4,
2 3 4
于是我们发现只要找到了关于i 最长的区间那么组合数就是此区间长度的等差数列。这样就可以保证j 指针在每次i 的遍历中不用回头,把时间复杂度将为了O(n).
import sys
first= sys.stdin.readline().strip()
n,d=list(map(int,first.split()))
line= sys.stdin.readline().strip().split()
b=list(map(int,line))
j=2
count=0
for i in range(n-2):
while j<n and b[j]-b[i]<=d:
j+=1
if j-i<=2:
continue
count=count+int((j-i-1)*(j-i-2)/2)
print(count%99997867)
原题链接(牛客网):
https://www.nowcoder.com/practice/c0803540c94848baac03096745b55b9b?tpId=137&tqId=33896&rp=1&ru=%2Fta%2Fexam-bytedance&qru=%2Fta%2Fexam-bytedance%2Fquestion-ranking&tab=answerKey
此类双指针的题目重点还是怎么统一处理每一个i,j 的组合。
相关类型题目还有:全零子串:https://www.lintcode.com/problem/number-of-substrings-with-all-zeroes/
作者; 星心火 致力于数据挖掘、全栈、AI研究。
欢迎有志同道合的朋友访问我的个人网站:
www.wikizhi.com
所有版权归星心火 所有,禁止商用!