替换数字:
给定一个字符串 s,它包含小写字母和数字字符,请编写一个函数,将字符串中的字母字符保持不变,而将每个数字字符替换为number。
例如,对于输入字符串 "a1b2c3",函数应该将其转换为 "anumberbnumbercnumber"。
对于输入字符串 "a5b",函数应该将其转换为 "anumberb"
输入:一个字符串 s,s 仅包含小写字母和数字字符。
输出:打印一个新的字符串,其中每个数字字符都被替换为了number
样例输入:a1b2c3
样例输出:anumberbnumbercnumber
数据范围:1 <= s.length < 10000。
STL中的resize()函数
resize()
适用于STL中大部分容器,用于改变STL容器的大小。如果重新指定的大小小于原大小(缩小容器),则会从后端删除元素;如果大于原大小(扩大容器),则会在后端添加元素。
resize()
的时间复杂度取决于容器类型和具体操作。复杂度视情况为O(n)或O(k),无论哪种都不算大,它是本题一种解法的基础
如何不使用额外的空间得到目标字符串?
如何判断某个字符是数字?
不使用额外空间,意味着只能操作原始字符串本身,显然第一步就是要给原字符串扩容,于是就要用到上面介绍的resize()
。那么具体扩容到多少呢?
这取决于字符串s中有多少数字,于是我们需要一个能判断字符是否是数字的函数:
bool isNum(char ch){
if(ch <= '9' && ch >= '0'){
return true;
}
return false;
}
各个字符的ASCII码不必记住,大部分时候只需要像上面一样借用字符常量来表示即可
如果实在需要判断,大部分时候现场测试一下也能知道答案
扩容操作:
字符串的数字个数只需要遍历一边即可得到,随后将其扩容至对应大小即可:
string s; cin >> s;
int count = 0;//统计数字的个数
for(int i = 0; i < s.size(); i++){
if(isNum(s[i])){
count++;
}
}
s.resize(s.size() + count * 5) //此处乘5是因为number占6个元素,再减去原来的数字所占空间
从后往前的巧妙遍历:
对于字符串a5b而言:
如此扩容后,我们自然想到只需要遍历一遍,将数字替换为number就好了。
问题是如果用正常的顺序遍历,为了不覆盖掉尚未遍历的部分,我们需要在每次数字替换时将后面的元素全部后移6位。这让复杂度瞬间来到O(n2)。如何规避呢?
从后往前遍历是解决类似问题很常见的方法,这个手法是收录这道题最主要的原因:
我们可以从扩容前的字符串尾部出发遍历,同时在新尾部设置一个指针now,用于指示当前最新的空位。如果检测到字母则直接放在s[now],如果是数字则在s[now]到s[now-5]位置分别赋值number六个字符:
这样就非常巧妙地规避了覆盖问题,这是个很重要的基础手法,一定要牢记。
下面是完整代码示例:
#include<bits/stdc++.h>
using namespace std;
//判断是否字符为数字
bool isNum(char ch) {
if (ch <= '9' && ch >= '0') {
return true;
}
return false;
}
int main() {
string s; cin >> s; int n = s.size();
int count = 0;//统计数字的个数
for (int i = 0; i < s.size(); i++) {
if (isNum(s[i])) {
count++;
}
}
s.resize(n + count * 5); //此处乘5是因为number占6个元素,再减去原来的数字所占空间
int oi = n - 1; int ni = s.size() - 1; //分别为新老字符串的遍历
//老指针遍历完0时结束
while (oi >= 0) {
if (isNum(s[oi])) {//是数字,在尾部赋值"number"
s[ni--] = 'r';
s[ni--] = 'e';
s[ni--] = 'b';
s[ni--] = 'm';
s[ni--] = 'u';
s[ni--] = 'n';
} else {//是字母,在尾部赋值对应字符
s[ni--] = s[oi];
}
oi--;
}
cout << s;
return 0;
}
ACM模式中巧用getchar()的解决方案
由于ACM模式中有输入输出过程,因此可以直接利用getchar()逐个字符读入,根据情况输出即可。直接变成送分题:
#include<bits/stdc++.h>
using namespace std;
//判断是否字符为数字
bool isNum(char ch) {
if (ch <= '9' && ch >= '0') {
return true;
}
return false;
}
int main() {
//读入第一个字符
char ch = getchar();
//读到换行符或文件末尾时结束
while(ch != '\n' && ch != EOF){
if(isNum(ch)){
cout << "number";
}else{
cout << ch;
}
ch = getchar();
}
return 0;
}