题意:给个字符串,给两种操作,操作1:将字符串的第pos个字符值改成c,操作2:求[l,r]中的不同的字符数量。
1.自己的思路
开26个数组记录每个字符出现的位置,对于操作1,二分找到要在原字符的数组中删除的位置,二分找到在新字符的数组中插入的位置。对于操作2,对于26个字符分别二分,求出是否存在[l,r]之间的。然后效率比较慢,2000ms的题目勉强1800ms过了。**
vector<vector<int> > v(26);
int main()
{
string s;
cin >> s;
f(i, 0, s.size() - 1)
{
int now = s[i] - 'a';
v[now].emplace_back(i);
}
int n;
int op, x;
char y;
cin >> n;
while (n--)
{
scanf("%d%d",&op, &x);
if (op == 1)
{
getchar();
scanf("%c", &y);
int now = s[x - 1] - 'a';
int pos = lower_bound(v[now].begin(), v[now].end(), x - 1) - v[now].begin();
v[now].erase(v[now].begin() + pos);
s[x - 1] = y;
pos = upper_bound(v[y - 'a'].begin(), v[y - 'a'].end(), x - 1) - v[y-'a'].begin();
v[y - 'a'].insert(v[y-'a'].begin()+pos,x-1);
}
else
{
int l = x;int r;scanf("%d", &r);
int ans = 0;
l--;
r--;
f(i, 0, 25)
{
int pos = lower_bound(v[i].begin(), v[i].end(), l) - v[i].begin();
if (pos == v[i].size())continue;
if (v[i][pos] <= r)ans++;
}
printf("%d\n", ans);
}
}
return 0;
}
2.正解,树状数组维护前缀
对于操作1,无非是将所有pos大于l的关于字符c的前缀加减1,直接用树状数组维护。对于操作2,则是直接对26个字符求区间是否存在。。。
int tree[26][N];
int n,m;
void add(int k, int c,int num)
{
for (int i = k;i <= m; i += i & -i)
tree[c][i] += num;
}
int read(int k,int c)
{
int sum = 0;
for (int i = k;i > 0; i -= i & -i)
sum += tree[c][i];
return sum;
}
int main()
{
string s,x;
cin >> x >> n;
s = '?' + x;
m = (int)s.size()-1;
f(i, 1, s.size()-1)
{
add(i, s[i] - 'a', 1);
}
int op, l, r;
char c;
f(i, 1, n)
{
scanf("%d%d", &op, &l);
if (op == 1)
{
getchar();
scanf("%c", &c);
add(l, s[l] - 'a', -1);
add(l, c-'a', 1);
s[l] = c;
}
else
{
scanf("%d", &r);
int ans = 0;
f(i, 0, 25)
{
if (read(r, i) - read(l - 1, i) >= 1)ans++;
}
printf("%d\n", ans);
}
}
return 0;
}