首先分析题意,即给定一个长度为N的字符串S,对其进行Q次操作,每次操作都是对字符串第 l 到 r 位置的字符的同时加 k。需要注意这里的移动是字符表的循环右移,即z+1=a。
关键问题:给定的Q 和 k 都很大,如果对每次的右移操作单独处理则需要最多2e5次对字符串的右移,很耗费时间,只能通过一部分测试用例。
关键解法:采用差分以及前缀和的方式,可以将右移次数降到O(1)。原因如下:可以利用Q次循环读取操作的时间,设置一个N + 1长度的数组prefix(记录字符串对应位置上字符应该移动的次数)在读取单次操作后,将移动区间的左端点累加 k ,移动区间的右端点的下一位累减 k,最后计算每一位的前缀和,其中如果有超过某一个移动区间的字符,将会减去累减的大小。
#include<cstdio>
#include<vector> // 引用容器
//可用typedef long long LL;来简写
using namespace std;
int main()
{
long long N, Q;
scanf("%lld %lld", &N, &Q);// 读取字符串长度 N 和操作次数 Q
//printf("%d %d", N, Q);
getchar(); // 抵消回车
vector<char> S(N); // 定义一个字符类型容器来存储字符串,大小初始化为 字符串的长度
vector<long long> prefix(N + 1); // 定义一个长整型的容器来存储前缀和,也是每一个字符应该右移的距离,大小为字符串的大小 + 1,方便记录超过移动区间的右端点的抵消值记录
for(long long i = 0; i < N; i++)
{
scanf("%c", &S[i]); // 读取字符串
}
getchar(); // 抵消回车
for(long long i = 0; i < Q; i++)
{
long long l, r, k;
scanf("%lld %lld %lld",&l, &r, &k); // 读取每次的操作区间[ l-1, r-1]
getchar(); // 抵消回车
prefix[l-1] += k; // 同时进行前缀和数组的初始化,将移动区间的左端点 累加 k
prefix[r] -= k; // 将移动区间的右端点的下一位累减 k,目的是将超过 右端点位置的字符右移的距离与之前累加的相抵消
}
for(long long i = 1;i <= N; i++)
{
prefix[i] += prefix[i-1]; // 将每一位的前缀和计算出来,能够实现将移动区间重叠部分累加,将超过部分累减
}
for(int i = 0; i < N; i++)
{
int a = S[i];
S[i] = (a - 'a' + prefix[i]) % 26 + 'a'; // 每一位字符S[i]与对应位置的移动距离 prefix[i]
}
for(long long i = 0; i < S.size(); i++)
{
printf("%c", S[i]);
}
return 0;
}