https://codeforces.com/contest/1234
D:给定一个字符串操作1更改该位置上的字符,2输出区间内有几个不同的字符
正常写法应该是线段树和树状数组,不过我忘了怎么写。。。用set可以做,以数字代替字母,'a'-0,'b'-1这样表示,在该字母表示的set容器插入出现该字母的所有位置
set用insert插入
例如:set<int>vis[30];这样表示有30个set容器;
本题有两个步骤:
(1)每更新一个元素,就删除原本在容器里面的相应位置,在新的容器里面插入该位置;
(2)最后通过迭代器访问区间,当然不是真正意义上的查询区间,区间为[L,R]通过lower_bound(L)查询每一个容器里面是否存在位置>=L的字母,这样就可以直接避免了去重,因为一个字母容器里面只会查询到一个数组下标>=L的该字母
需要注意的是 二分查找一个有序数列,返回第一个大于等于x的数,如果没找到,返回末尾的迭代器位置
也就是说如果在某一个字母容器查询不到在区间[L,R]的位置,会返回setname.end(),每一次二分查找的时候判断一下即可,否则查询不到他仍然会返回一个数,即该容器的数组下标
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
inline int read(){
int X=0,w=0;char ch=0;
while(!isdigit(ch)){w|=ch=='-';ch=getchar();}
while(isdigit(ch))X=(X<<3)+(X<<1)+(ch^48),ch=getchar();
return w?-X:X;
}
/*------------------------------------------------------------------------*/
const int maxn=1e5;
char s[maxn];
int main( )
{
//ios_base::sync_with_stdio(0); cin.tie(0); cout.tie(0);
//freopen("a.txt","r",stdin);
//freopen("a.txt","w",stdout);
set<int>vis[30];
//cin>>str;
scanf("%s",s+1);
for(int i=1;s[i];++i){
vis[s[i]-'a'].insert(i);
}
int q;
int op;
cin>>q;
//set会自动排序
while(q--){
//cin>>op;
scanf("%d",&op);
if(op==1){
int pos;//位置
char ch;//插入的字符
//cin>>pos>>ch;
scanf("%d %c",&pos,&ch);
vis[s[pos]-'a'].erase(pos); //先清空原本在此位置的字符
vis[ch-'a'].insert(pos);
s[pos]=ch;
}
else if(op==2){
int ans=0;
int l,r;
//cin>>l>>r;
scanf("%d %d",&l,&r);
for(int i=0;i<26;++i){
set<int>::iterator it=vis[i].lower_bound(l);
if(it==vis[i].end())continue;
if(*it>=l&&*it<=r){
ans++;
}
/*for(int j=l;j<=r;++j){
set<int>::iterator it=vis[i].find(j);
if((*it)==i)continue;
if(*it>=l&&*it<=r){
ans++;
break;
}
}*/
}
cout<<ans<<endl;
}
}
return 0;
}