浅谈尺取法
例题一:
http://acm.hdu.edu.cn/showproblem.php?pid=5056
题目大意翻译:
给你一个由小写字母组成的字符串,使得子串中每个小写字母的数目不超过k,你的任务是计算这样的子串的数目
分析:
枚举字符串下标i,每次计算以i为结尾的符合条件的最长串。
那么以i为结尾的符合条件子串个数就是最长串的长度。求和即可
现在考虑如何算以i结尾的最长串
好吧其实这就是个尺取法
聪明的你一定看了code就会的
code:
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int Max= 100010;
char str[Max];
int vis[30];
int main()
{
int k,T,len;
LL sum;
scanf("%d",&T);
while(T--)
{
scanf("%s",str);
scanf("%d",&k);
memset(vis,0,sizeof(vis));
len = strlen(str);
int l=0;sum=0;
for(int i=0;i<len;i++)
{
vis[str[i]-'a']++;
while(vis[str[i]-'a']>k)vis[str[l]-'a']--,l++;
sum+=(i-l+1);
}
cout<<sum<<endl;
}
return 0;
}
例题二:
http://poj.org/problem?id=3061
题目大意:给出一个序列,求区间和大于或者等于S的最短区间长度的最小值
结合上一题,
还是考虑以i结尾的区间和大于等于S的区间长度的最小值
part code:
for (int l=1,r=0,now=0;l<=n;){
while (now<S&&r<n) now+=a[++r]; //表示先把r+1,再执行now+=a[r].注意要使r<=n.
/*当now<S的时候,我们不断地向右延伸区间,直到now>=S*/
if (now<S) break;
/*这时候l已经过大了,r到n也满足不了条件,这时要退出循环,否则下面的ans会被错误更新.*/
ans=min(ans,r-l+1); // [l,r]区间的长度
now-=a[l++]; // 这里就是先执行now-=a[l],然后再执行l+=1.
}
例题三:
题目大意:题目要求找到一个尽量长的连续序列,使得该序列中没有相同的元素
分析:
从左到右首先尽量延长right,right无法延长时再继续把left向右移动一个,然后重复以上过程,每次再取ans最大值即可。另外再判断重复时可采用数据结构,利用set集合十分方便
#include<cstdio>
#include<cstdlib>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<set>
using namespace std;
#define maxn 1000005
int a[maxn];
set<int> s;
int main()
{
// freopen("input.txt","r",stdin);
int t,n;
scanf("%d",&t);
while(t--){
s.clear();
scanf("%d",&n);
int i,l=0,r=0,ans=0;
for(int i=0;i<n;i++)
{
scanf("%d",&a[i]);
}
while(r<n){
while(r<n&&!s.count(a[r])) s.insert(a[r++]);
ans=max(ans,r-l);
s.erase(a[l++]);
}
printf("%d\n",ans);
}
return 0;
}
总之:
尺取法很简单,处理一些不定区间或子集问题