原题链接:https://ac.nowcoder.com/acm/contest/76609/F
时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 262144K,其他语言524288K
64bit IO Format: %lld
题目描述
小红定义一个字符串是“好串”,当且仅当该字符串不包含长度不小于 2 的回文子串。
现在小红拿到了一个仅包含"red"三种字符的字符串,她有如下两个操作:
· 输入 1 x chr:将第x个字符修改为chr
· 输入 2 l r:询问第l个字符到第r个字符的子串再修改最少多少个字符可以变成好串(每次修改后也必须是"red"三种字符中的一种)。
你能帮帮她吗?
输入描述:
第一行输入两个正整数n,q,代表字符串长度、操作次数。 第二行输入一个仅包含长度为n的、"red"三种字符的字符串。 接下来的q行,每行输入一个操作1 x chr或者2 l r,含义如上所述。 1≤n,q≤10^5 1≤x≤n 1≤l≤r≤n chr∈′r′,′e′,′d′
保证至少有一次操作 2。
本题有5%的数据满足:1≤n,q≤5
有25%的数据满足:1≤n,q≤2000
另外有20%的数据满足:所有操作均为操作2。
输出描述:
对于每个操作 2,输出一个整数表示答案,即子串变成好串的最小修改次数。
示例1
输入
5 3 deder 2 2 4 1 3 r 2 1 3
输出
1 0
说明
第一次询问的子串是"ede",将第一个或者第三个字符修改为'r'可以变成好串。 第二次询问的子串是"der",是好串。
解题思路:
首先根据题意说了不能存在长度大于等于2的回文串,实际上就是说任意连续的三个字符都不相同,知道了这个结论之后就很简单了,那么对于任意一个字符串的前三个字符就只有六种情况了,为[red,rde,erd,edr,dre,der],前三个字符确定之后实际上后面的字符也就唯一确定了,例如当前三个字符为red时,由于任意的连续三个字符都要不同,所以第四个字符应该为r,第五个字符应该为e,第六个字符应该为d,然后你就会发现一个规律,就是只要前三个字符确定了,后面的字符就会按照前三个字符一直循环,这里就是redredred......,由于前三个字符只有六种情况,那么总的合法字符串就只有六种,不管我们进行修改还是查询操作,我们都只需要对这六种情况进行维护即可,对于修改操作,我们对这六种情况维护一个前缀分别需要修改的字符数,对于查询操作,不管查询的是哪个区间,这个区间也只会有六种情况,我们对这个区间也只需要查询这六种情况即可,对于上述区间维护问题,可以等价转换为俩个前缀相减,那么可以使用六个树状数组就行维护。
时间复杂度:O(nlog(n)),树状数组维护的时间为O(nlog(n))。
空间复杂度:O(n)。
cpp代码如下:
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <unordered_map>
#include <vector>
#include <cmath>
using namespace std;
typedef long long LL;
typedef pair<int,int>PII;
const int N=1e5+10,M=N*2,mod=1e9+7;
int n,m,k;
char g[N];
string s[6]={"red","rde","erd","edr","dre","der"};
int tr[6][N];
int lowbit(int x)
{
return x&-x;
}
void modify(int x,int c,int tr[])
{
for(int i=x;i<=n;i+=lowbit(i))tr[i]+=c;
}
int query(int x,int tr[])
{
int res=0;
for(int i=x;i;i-=lowbit(i))res+=tr[i];
return res;
}
int main()
{
cin>>n>>m;
scanf("%s",g+1);
for(int i=0;i<6;i++)
{
for(int j=1;j<=n;j++)
if(g[j]!=s[i][(j-1)%3])modify(j,1,tr[i]); //当前位置字符和六种情况中的某个情况这个位置字符不同,所以维护的这种情况这个位置需要进行一次修改操作
}
while(m--)
{
int op;
scanf("%d",&op);
if(op==1){
int x;
char ch[2];
scanf("%d%s",&x,ch);
char ch2=g[x];
g[x]=ch[0];
//维护六种情况
for(int i=0;i<6;i++){
//这个位置开始相同变为不同,这个位置修改次数需要加1
if(ch2==s[i][(x-1)%3] && g[x]!=s[i][(x-1)%3])modify(x,1,tr[i]);
//这个位置开始不同变为相同,这个位置修改次数减1
if(ch2!=s[i][(x-1)%3] && g[x]==s[i][(x-1)%3])modify(x,-1,tr[i]);
}
}else {
int l,r;
scanf("%d%d",&l,&r);
int ans=1e9;
//查询某个区间对于变为六种情况的哪一种情况修改次数最少
for(int i=0;i<6;i++){
int q=query(r,tr[i])-query(l-1,tr[i]);
ans=min(ans,q);
}
cout<<ans<<endl;
}
}
return 0;
}