牛客寒假基础集训营 | Day1 G-eli和字符串

G-eli和字符串

题目描述
eli拿到了一个仅由小写字母组成的字符串。
她想截取一段连续子串,这个子串包含至少 k k k kkk kkk 个相同的某个字母
她想知道,子串的长度最小值是多少?
注:所谓连续子串,指字符串删除头部和尾部的部分字符(也可以不删除)剩下的字符串。例如:对于字符串“arcaea”而言,“arc”、“rcae”都是其子串。而“car”、“aa”则不是它的子串。
输入描述:
第一行输入两个正整数 n n n nnn nnn k k k kkk kkk 1 ≤ k ≤ n ≤ 2000001 ≤ k ≤ n ≤ 2000001 ≤ k ≤ n ≤ 200000 1≤k≤n≤2000001 \le k \le n \le 2000001≤k≤n≤200000 1kn2000001kn2000001kn200000
输入仅有一行,为一个长度为 n n n nnn nnn 的、仅由小写字母组成的字符串。
输出描述:
如果无论怎么取都无法满足条件,输出 − 1 − 1 − 1 −1-1−1 111
否则输出一个正整数,为满足条件的子串长度最小值。

示例1
5 2
abeba

3
说明:选择“beb”子串,长度为3,其中包含相同的两个’b’

思路如下

方法类似于“尺取法”,也可以认为是尺取法 或 双针法,其实大致思路都一样,只不过是实现的方法不一样,,,它们的主要思路是:

  1. 先找到一个符合题意的区间,在这一题中就是 含有 k 个相同字母的区间。
  2. 之后在 保证这个区间符合题意的基础上不断缩小区间范围,去找到更短的区间 的答案,当该区间在缩小完之后 不符和题意了,那么在扩大这个区间,使该区间在此符合题意,此时再次进行 2 这一步操作

题解一(尺取法?)

#include<iostream>
#include<map>
#include<algorithm>
using namespace std;
const int Len = 200005;
int barrel[Len];
char ar[Len];
 
int main()
{
    //freopen("T.txt","r",stdin);
    map<char , int> mp;
    int n,k;
    cin >> n >> k >> ar;
    int l = -1, r = -1;
    //尺取 找到一个合适的区间
    for(int i = 0; i < n; i ++)
    {
        mp[ar[i]] ++;
        if(mp[ar[i]] == k)
        {
            r = i;
            break;
        }
    }
    if(r == -1)
    {
        cout<< -1 << endl;
        return 0;
    }
    //不断缩紧、扩大区间
    int len = 1e9;
    while(l < r && l < n - k && r < n)
    {
        while(mp[ar[r]] == k && l < r)
        {
            len = min(len , r - l);
            l ++;
            mp[ar[l]] --;
        }
 
        r ++;
        if(r == n) break;
        mp[ar[r]] ++;
    }
    if(len == 1e9)
        cout<<-1<<endl;
    else
        cout<<len<<endl;
 
    return 0;
}

题解如下(双针法? 前缀和?)

#include<iostream>
#include<string>
using namespace std;
const int Len = 2e5 + 5;
int dp[Len][26];

int main()
{
    int n,k;
    string s;
    cin >> n >> k >> s;

    dp[0][s[0] - 'a'] = 1;
    for(int i = 1; i < n; i ++)
    {
        for(int j = 0; j < 26; j ++)
        {
            dp[i][j] = dp[i - 1][j];
        }
        dp[i][s[i] - 'a'] ++;
    }

    //先找到一个合适的区间
    int min_len = 1e9;
    for(int i = 0; i < 26; i ++)
    {
        int l = 0,r = 0;
        if(dp[n - 1][i] < k) continue;
        while(l < n && dp[l][i] == 0) l ++;
        while(r < n && dp[r][i] < k)  r ++;
        min_len = min(min_len , r - l  + 1);

        //对区间不断 扩大缩小范围
        for(l ++; l < n; l ++)
        {
            if(s[l - 1] - 'a' == i)
            {
                r ++;
                while(r < n && s[r] - 'a' != i) r ++;
                if(r == n)  break;
            }
            min_len = min(min_len , r - l  + 1);
        }
    }
    if(min_len != 1e9)
        cout << min_len << endl;
    else
        cout << -1 << endl;

    return 0;
}
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值