【237题】算法基础精选题单 刷题笔记DAY2 尺取法

NC18386 字符串

链接:
https://ac.nowcoder.com/acm/problem/18386
来源:牛客网

题目描述
小N现在有一个字符串S。他把这这个字符串的所有子串都挑了出来。一个S的子串T是合法的,当且仅当T中包含了所有的小写字母。小N希望知道所有的合法的S的子串中,长度最短是多少。
输入描述:
一行一个字符串S。只包含小写字母。S的长度不超过106.
输出描述:
一行一个数字,代表最短长度。数据保证存在一个合法的S的子串。
示例1
输入
ykjygvedtysvyymzfizzwkjamefxjnrnphqwnfhrnbhwjhqcgqnplodeestu
输出
49

利用队列去维护一个包含26个小写字母的区间,并在满足26字母的条件下缩小区间范围,当不满足26字母的时候,再次向右延长区间(滑动窗口)

用队列维护滑动窗口思路较为清晰,当然也可用双指针维护,甚至手写队列维护。

下面给出STL队列维护的代码

#include <bits/stdc++.h>
using namespace std;
#define int long long

const int N = 2e5+10;
int n, m;
map<char,int>M;
queue<int>q;
string s;
signed main()
{
    cin>>s;
    int have=0;
    int ans=0x3f3f3f3f;
    for(int i=0;i<26;i++)
      M[i+'a']=0;
    for(int i=0;i<s.size();i++)
    {
        //cout<<q.size()<<"--"<<have<<endl;
        q.push(s[i]);
        if(!M[s[i]]) have++;
         M[s[i]]++;
        while(have==26&&q.size())
        {
            ans=min(ans,(int)q.size());
            char hh=q.front();
            q.pop();
            if(!--M[hh]) {have--;break;}
        }
        
    }
    cout<<ans<<endl;
}

NC207040 丢手绢

链接:https://ac.nowcoder.com/acm/problem/207040
来源:牛客网

题目描述
“丢丢手绢,轻轻地放在小朋友的后面,大家不要告诉她,快点快点抓住她,快点快点抓住她。”
牛客幼儿园的小朋友们围成了一个圆圈准备玩丢手绢的游戏,但是小朋友们太小了,不能围成一个均匀的圆圈,即每个小朋友的间隔可能会不一致。为了大家能够愉快的玩耍,我们需要知道离得最远的两个小朋友离得有多远(如果太远的话牛老师就要来帮忙调整队形啦!)。
因为是玩丢手绢,所以小朋友只能沿着圆圈外围跑,所以我们定义两个小朋友的距离为沿着圆圈顺时针走或者逆时针走的最近距离。
输入描述:
第一行一个整数N,表示有N个小朋友玩丢手绢的游戏。
接下来的第2到第n行,第i行有一个整数,表示第i-1个小朋友顺时针到第i个小朋友的距离。
最后一行是第N个小朋友顺时针到第一个小朋友的距离。
输出描述:
输出一个整数,为离得最远的两个小朋友的距离。
示例1
输入
3
1
2
3
输出
3
备注:
2

𝑁

100000
2≤N≤100000
距离和(圆圈周长)小于等于2147483647

题目要求求任意两个小登的最远距离,那么在环形区域中,题目是怎么定义距离的呢?

题目提到顺逆时针的最小值,也就是距离必定为劣弧,也就是说距离的变化呈现二次函数,具有最值,而这个最值是已知的,且距离大于L/2之后可以直接结束更新最大距离(因为距离只可能会变小)

那么我们就可以枚举每一个小登,并维护一段长度不超过L/2的区间,利用这个区间更新最大值答案

运用化曲为直的思想将数组长度翻倍,就可以很方便去维护每一个原弧上的区间
总结来说,感觉尺取法就是滑动窗口,维护一个区间窗口去解决问题,窗口具有某些性质,维护即为我们需要保持这个性质

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 2e5 + 10;
int n, m;
int s[N * 2];
signed main()
{
  cin >> n;
  int L = 0;
  for (int i = 1; i <= n; i++)
  {
    cin >> s[i];
    s[i + n] = s[i];
    L += s[i];
  }
  int  ans = 0;
  int cnt = 1;
  int  sum = 0;
  for (int i = 1; i <= n; i++)
  {
    while (sum < L / 2) 
      sum += s[cnt++];
    ans = max(ans, min(sum, L - sum));
    sum -= s[i];
  }
  cout << ans << endl;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值