CodeForces ~ 999C ~ Alphabetic Removals (模拟 + 前缀和)

本文介绍了一个字符串处理问题的高效解决方案,通过统计字符频率并利用前缀和的方法来避免暴力模拟导致的时间超支。该方法特别适用于需要多次删除特定字符直至达到指定状态的问题。

题意:

给你一个长度为n的字符串,操作k次,输出最终的串。操作为:每次删除一个字符,从左往右删除,按照a~z的顺序,有a删a,没a删b,没a,b删c…


思路

暴力模拟,肯定超时。假设最后一次操作删的是C字符,那么比C字符小的一定被删了,比C字符大的一定没被删除。C字符被删除了一部分,某个C字符之前的C字符都被删了。所以我们只需要计算出最后一次的C字符是哪个,是第几个C字符即可。
所以统计每个字符出现的次数,然后做前缀和a[]。从a~z,如果a[i]>=ka[i]>=k说明最后一次删除的是ii 字符,是第ka[i1]ii字符,按照这个模拟输出即可。

#include <bits/stdc++.h>
using namespace std;
const int MAXN = 1e5+5;
int n, k, a[26];
string str;
int main()
{
    cin >> n >> k >> str;
    for (int i = 0; i < n; i++) a[str[i]-'a']++;
    for (int i = 1; i < 26; i++) a[i] += a[i-1];
    int pos = 0;
    for (int i = 0; i < 26; i++) if (a[i] >= k)
    {
        pos = i; k -= a[i-1];
        break;
    }
    for (int i = 0; i < n; i++)
    {
        if (str[i]-'a' < pos) continue;//小于pos的
        if (str[i]-'a' > pos) cout << str[i];//大的pos的
        else
        {
            if (k) k--;
            else cout << str[i];
        }
    }
    return 0;
}
/*
15 3
cccaabababaccbc
*/
### 关于Codeforces中的前缀和相关题目及其解题思路 #### 什么是前缀和前缀和是一种常见的算法技巧,用于加速区间求和操作。对于数组 `A` 的前缀和定义如下: \[ \text{prefix}[i] = \sum_{j=1}^{i} A[j], \quad (1 \leq j \leq i) \] 利用前缀和可以快速计算任意区间的和 \( S[l..r] = \text{prefix}[r] - \text{prefix}[l-1] \)[^5]。 --- #### Codeforces 中涉及前缀和的经典题目及解法 ##### **题目一:Codeforces1398C Good Subarrays** 该题要求统计满足特定条件的连续子序列数量。具体来说,给定一个长度为 \( n \) 的整数序列 \( a_1, a_2, ..., a_n \),找出所有满足以下条件的子序列数目: \[ \sum_{i=l}^{r} a_i = r - l + 1 \] ###### 解题思路 为了高效解决此问题,可以通过构建前缀和数组并记录其差值频率的方式完成。设当前遍历到位置 \( i \),则目标转化为寻找之前某个位置 \( j \) 满足: \[ \text{prefix}[i] - \text{prefix}[j-1] = i - j + 1 \] 即: \[ \text{prefix}[i] - i = \text{prefix}[j-1] - (j-1) \] 因此可以用哈希表存储每种可能的 \( \text{prefix}[k] - k \) 出现次数,在线性时间内完成计数。 ```cpp #include <bits/stdc++.h> using namespace std; int main(){ int n; cin >> n; vector<int> a(n); for(auto &x : a) cin >> x; long long res = 0; unordered_map<long long, int> freq; freq[0]++; long long prefix_sum = 0; for(int i=0;i<n;i++){ prefix_sum += a[i]; res += freq[prefix_sum - (i+1)]; freq[prefix_sum - (i+1)]++; } cout << res; } ``` --- ##### **题目二:CodeForces21C - Stripe 2** 在这道题中,我们需要判断是否存在一种分割方式使得整个数组被划分为三个部分,每一部分的元素之和相同。 ###### 解题思路 首先验证总和是否能被三整除。如果不能,则直接返回零作为答案;否则继续尝试找到两个合适的切割点使两段分别等于总和三分之一即可[^4]。 我们可以借助两次扫描的方法来实现这一逻辑——第一次从前向后累积直到达到期望的目标值为止;第二次再重复类似过程但要跳过已选区域之外的部分。 ```cpp #include<stdio.h> const int MAXN=1e5+7; long long sum[MAXN]; int main(){ int n,a; while(~scanf("%d",&n)){ memset(sum,0,sizeof(sum)); bool flag=false; int cnt=0; for(int i=1;i<=n;++i){ scanf("%d",&a); sum[i]=sum[i-1]+a; } if(sum[n]%3!=0 || n<3){ printf("0\n"); continue; } long long target=sum[n]/3; for(int i=1;i<=n && !flag;i++)if(sum[i]==target)cnt++,flag=true; flag=false; for(int i=n;i>=1 && !flag;i--)if(sum[i]==2*target)cnt*=1,flag=true; printf("%lld\n",cnt); } return 0; } ``` --- ##### **其他常见应用场景** 除了上述例子外,还有许多场景适合应用前缀和: 1. **最大/最小子数组和** 使用 Kadane's Algorithm 结合动态规划思想可有效解答此类最优化问题。 2. **多维范围查询** 对二维甚至更高维度矩阵扩展基础概念形成积木式解决方案,比如二维前缀和能够支持矩形区域内数值迅速获取。 3. **滑动窗口技术配合使用** 当遇到固定大小或者变化不定边界约束下的局部性质分析时尤为有用。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值