本题是典型的“滑动窗口”题,所以借由本题复习一下“滑动窗口算法”。
1.算法本质:贪心、利用之前的状态求解当前状态。
2.适用条件:区间具有单调性,即若[L,R]满足条件, [L-1,R]必满足条件,并且[R,L+1]有可能满足条件。
3.可以解决的问题:找一个具有单调性的区间的最大值或者最小值。
先上代码。
#include<bits/stdc++.h>
#define FAST ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
#define INF 0x3f3f3f3f
typedef long long ll;
const int maxn = 4e5+5;
using namespace std;
int k;
char s[maxn];
int main()
{
FAST;
while(cin>>k)
{
cin>>s+1;
int len=strlen(s+1);
int l=1,r=1,ans=-INF;
int cnt[27]={0};
cnt[s[1]-96]++;
while(l<=r && r<=len)
{
int flag=1;
for (int i=1; i<=26; i++)
{
if (cnt[i]>k)
{
flag=0;
break;
}
}
if (flag==1)
{
if (ans<r-l+1) ans=r-l+1;
r++;
cnt[s[r]-96]++;
}
else
{
cnt[s[l]-96]--;
l++;
}
}
cout<<ans<<endl;
}
return 0;
}
后来又写了一个自认为更快地版本(然而更慢)
#include<bits/stdc++.h>
#define FAST ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
#define INF 0x3f3f3f3f
typedef long long ll;
const int maxn = 4e5+5;
using namespace std;
int k;
char s[maxn];
int main()
{
FAST;
while(cin>>k)
{
map<char, int> cnt;
cnt.clear();
cin>>s+1;
int len=strlen(s+1);
int l=1,r=1,ans=-INF;
cnt[s[1]]++;
while(l<=r && r<=len)
{
while(cnt[s[r]]>k && l<=r) cnt[s[l++]]--;
ans=max(ans,r-l+1);
cnt[s[++r]]++;
}
cout<<ans<<endl;
}
return 0;
}
复习一下模板
模板一:
int l=1,r=1; //左右端点同时从最左边开始取
while(l<=r && r<=n)
{
if (满足条件)
{
if (r-l+1<ans)
{
ans=r-l+1;
ans_l=l, ans_r=r;
}
r++; (l++)
更新窗口
}
else //不满足条件
{
l++; (r++)
更新窗口
}
}
模板二:
int l=1,r=1; //左右端点同时从最左边开始取
讲第一个元素加入窗口,并对窗口进行更新;
while(l<=r && r<=n)
{
while(加入a[r]后窗口不符合条件)
{
l++; 或者r++;
更新窗口; //弹出或加入元素
}
更新ans;
r++; 或l++ //贪心的扩展(缩小)窗口
更新窗口; //弹出或加入元素
}
究竟是l++还是r++要看求的是最长区间还是最短区间
题单:
UVA 11572 Unique Snowflakes
洛谷 p1102 A-B数对
洛谷 p1638
poj 3061 Sequence
Codeforces 1042D Petya and Array
Codeforces 47E Cannon
Codeforces 939E Maximize!
Atcoder 4142 Xor Sum 2
更多讲解和题目:
https://www.luogu.com.cn/blog/Nero-Yuzurizaki/chi-qu-fa-xiao-jie