1.双指针
在区间操作时,利用两个下标同时便利,进行高效操作,可将O(n2)时间降低到O(n)
根据指针移动的方向,分为反向扫描和同向扫描
反向扫描
一般用于有序数组或者字符串类问题
对于有序数组,见我day1文章中的“纪念品分组”一题的解题思路,运用的就是反向扫描思想
对于字符串一类, 对应题目:回文判断
#方法一(使用双指针的思想)
import os
import sys
s=input()
l,r=0,len(s)-1
ok='Y'
while l<=r:
if s[l]==s[r]:
l+=1
r-=1
else:
ok='N'
break
print(ok)
#方法二(直接翻转字符串)
s=input()
if s==s[::-1]:
print('Y')
else:
print('N')
同向扫描
被称为滑动窗口,根据题给条件,维护一个[left,right]区间的信息,如区间和,各个元素个数,直至满足条件时停止滑动
对应题目1:美丽的区间
import os
import sys
n,S=map(int,input().split())
a=list(map(int,input().split()))
#找最短区间满足区间之和>=S
min_len=n+1
#[left,right)
left,right=0,0
#tot表示滑动窗口【left,right)之间的区间和
tot=0
while left<n:
#不断扩展右端点,直至区间和>=S
while right<n and tot<S:
tot+=a[right]
right+=1
if tot>=S:
min_len=min(min_len,right-left)
tot-=a[left]
left+=1
if min_len==n+1:
min_len=0
print(min_len)
对应题目2:挑选子串
import os
import sys
n,m,k=map(int,input().split())
a=list(map(int,input().split()))
ans=0
left,right=0,0
num=0
#num表示滑动窗口中大于等于m的元素个数
while left<n:
#不断扩展右端点,直至区间恰好有k个数字
while num<k and right<n:
if a[right]>=m:
num+=1
right+=1
if num>=k:
ans+=(n-right+1)
#直接在原来的num上进行计算
if a[left]>=m:
num-=1
left+=1
print(ans)
2.二分
二分法
每次将搜索的范围缩小一半,可以在O(log n)实时间内找到正确答案
二分法的前提条件:具有单调性(利用单调性调整二分算法查找的区间)
步骤:
1.选取候选区间[left,right]
2.不断循环,直至区间满足特定条件
——计算中点mid=(left+right)/2
——判断中点是否合法,根据中点的计算结果调整[left,right]
浮点二分
eg.计算根号2
left,right=1,2
while (right-left)>=1e-5:
mid=(left+right)/2
if mid*mid>2:
right=mid
else:
left=mid
二分答案
题目所求答案(一般为整数)具有单调性质,采用猜答案+二分
步骤:
#二分答案 半伪代码
def check(x):
#判断x是否合法,合法返回True,否则返回False
pass
left,right,ans=初始化
while left<=right:
mid=(left+right)//2
if check(mid):
ans=mid
left=mid+1
else:
right=mid-1
print(ans)
相关题目1:分巧克力
import os
import sys
N,K=map(int,input().split())
a=[]
for i in range(N):
x,y=map(int,input().split())
a.append((x,y))
#合法条件:假定切出的巧克力边长为x,能否切出x块
def check(x):
#cnt表示能切出的块数
cnt=0
for H,W in a:
cnt+=(H//x)*(W//x)
return cnt>=K
#边长搜索区间为[left,right]
left,right=1,100000
ans=1
while left<=right:
mid=(left+right)//2
if check(mid):
ans=mid
left=mid+1
else:
right=mid-1
print(ans)
相关题目二:跳石头
import os
import sys
#演示下标 #最短跳跃距离
#0,2,11,14,17,21,25,=》 2
#
#0,11,14,17,21,25 =》3
L,N,M=map(int,input().split())
a=[]
for i in range(N):
d=int(input())
a.append(d)
#假设最短跳跃距离为x,移除的岩石数量不超过M,则为合法
def check(x):
#cnt 表示移除岩石的数量
cnt=0
#;ast_idx表示上一个位置
last_idx=0
for i in range(N):
if a[i]-last_idx>=x:
last_idx=a[i]
else:
cnt+=1
#最后一步的特判
if L-last_idx<x:
return False
return cnt<=M
left,right=1,L
ans=-1
while left<=right:
mid=(left+right)//2
if check(mid):
ans=mid
left=mid+1
else:
right=mid-1
print(ans)