算法解析—同向双指针 字节笔试 万万没想到抓捕孔联顺,列表最大间隔不超过D

算法解析—同向双指针

字节笔试 万万没想到抓捕孔联顺,列表子元素最大间隔不超过D

最近在刷题时就看到这题,题目描述十分中二。
我叫王大锤,是一名特工。我刚刚接到任务:在字节跳动大街进行埋伏,抓捕恐怖分子孔连顺。和我一起行动的还有另外两名特工,我提议

  1. 我们在字节跳动大街的N个建筑中选定3个埋伏地点。
  2. 为了相互照应,我们决定相距最远的两名特工间的距离不超过D。

我特喵是个天才! 经过精密的计算,我们从X种可行的埋伏方案中选择了一种。这个方案万无一失,颤抖吧,孔连顺!
……
万万没想到,计划还是失败了,孔连顺化妆成小龙女,混在cosplay的队伍中逃出了字节跳动大街。只怪他的伪装太成功了,就是杨过本人来了也发现不了的!

请听题:给定N(可选作为埋伏点的建筑物数)、D(相距最远的两名特工间的距离的最大值)以及可选建筑的坐标,计算在这次行动中,大锤的小队有多少种埋伏选择。
注意:

  1. 两个特工不能埋伏在同一地点
  2. 三个特工是等价的:即同样的位置组合(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
所有版权归星心火 所有,禁止商用!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值