【无标题】

发布于:2025-04-05 ⋅ 阅读:(17) ⋅ 点赞:(0)

题目

题源:第十四届蓝桥杯(4960. 子串简写 - AcWing题库

程序猿圈子里正在流行一种很新的简写方法:

对于一个字符串,只保留首尾字符,将首尾字符之间的所有字符用这部分的长度代替。

例如 internationalization 简写成 i18n,Kubernetes 简写成 K8s,Lanqiao 简写成 L5o 等。

在本题中,我们规定长度大于等于 K
 的字符串都可以采用这种简写方法(长度小于 K
 的字符串不配使用这种简写)。

给定一个字符串 S和两个字符 c1和 c2
,请你计算 S
 有多少个以 c1
 开头 c2
 结尾的子串可以采用这种简写?

输入格式
第一行包含一个整数 K
。

第二行包含一个字符串 S
 和两个字符 c1
 和 c2
。

输出格式
一个整数代表答案。

数据范围
对于 20%
 的数据,2≤K≤|S|≤10000
。
对于 100%
 的数据,2≤K≤|S|≤5×105
。S
 只包含小写字母。c1
 和 c2
 都是小写字母。
|S|
 代表字符串 S
 的长度。

输入样例:
4
abababdb a b
输出样例:
6
样例解释
符合条件的子串如下所示,中括号内是该子串

[abab]abdb
[ababab]db
[abababdb]
ab[abab]db
ab[ababdb]
abab[abdb]

暴力解法

#include <iostream>
using namespace std;
typedef long long LL;
const int N = 5e5 + 10;
LL sum[N];
string s;
int k;
char c1, c2;
int main()
{
    cin >> k;
    cin >> s;
    cin >> c1 >> c2;
    int n = s.size();
    int ret = 0;
    for(int i = 0;i < n;i++)
    {
        if(s[i] != c1) continue;
        for(int j = i + 1;j < n;j++)
        {
            if(s[j] == c2)
            {
                int t = j - i + 1;
                if(t >= k) ret++;
            }
        }
    }
    cout << ret << endl;
    return 0;
}

标准解法(前缀和+双指针)

#include <iostream>
using namespace std;
typedef long long LL;
const int N = 5e5 + 10;
LL cnt[N]; //统计k个字符后每个位置后有多少个c2
string s;
int k;
char c1, c2;
int main()
{
    cin >> k >> s >> c1 >> c2;
    int n = s.size();
    LL ret = 0;
    for(int i = n - 1;i >= k - 1;i--)
    {
        if(s[i] == c2) cnt[i] = cnt[i + 1] + 1;
        else cnt[i] = cnt[i + 1];
    }
    
    for(int i = 0;i <= n - k;i++)
    {
        if(s[i] != c1) continue;
        int t = i + k - 1;
        while(t < n && s[t] != c2) t++;
        ret += cnt[t];
    }
    cout << ret << endl;
    return 0;
}
//好题

题解

此题暴力解法非常好像,固定一边两层for循环就结束了。

但是标准解法里面前缀和的思想就不是那么好想了,从后往前的写法比较好写,往后计算应该也可以,但是我自己并没有尝试过。

整个题目思路是先处理k个字符后每个当前位置往后找有多少个c2字符,这样预处理,我们在计算每个c1能有多少个满足条件的子串的时候就可以快速得出,从本题可以得出的结论:在遍历的时候,需要快速计算某样东西时,可以思考前缀和,而不是只有求和的时候才能用。