【剑指Offer-字符串】面试题5:替换空格
题目描述
请实现一个函数,将一个字符串中的每个空格替换成“%20”。例如,当字符串为We Are Happy.则经过替换之后的字符串为We%20Are%20Happy。
思路1
如果不要求原地替换的话,设置一个新的字符串 ans,遍历输入的字符串 s,如果 s[i] 为空格,则 ans += "%20",否则的话 ans += s[i]。代码如下:
class Solution {
public:
string replaceSpace(string s) {
if(s.empty()) return s;
string ans = "";
for(int i=0; i<s.size(); i++){
if(s[i]!=' ') ans += s[i];
else ans += "%20";
}
return ans;
}
};
思路2
一种比较容易想到的方法就是从左到右遍历该字符串,当碰到空格的时候就把空格替换为%20,因为从1个字符(' ')替换为了3个字符('%20'),所以要对空格后面的字符进行移动。假设字符串长度为n,对于每个空格字符,需要移动后面的O(n)的字符,因此对于含有O(n)个空格字符的字符串而言,总的时间复杂度为\(O(n^2)\)。
思路1对应的代码如下
class Solution {
public:
// str为输入的字符串,length为总的可用长度
void replaceSpace(char *str,int length) {
if(str==nullptr || length==0)
return;
int strLen = 0; //字符串长度
while(str[strLen]!='\0')
strLen++;
for(int i=0; i<strLen; i++){
if(str[i]==' '){
for(int j=strLen; j>i; j--)
str[j+2] = str[j]; //注意是加2
str[i] = '%';
str[i+1] = '2';
str[i+2] = '0';
strLen += 2;
}
}
}
};
思路3
思路1的时间复杂度太高了,思路1是从左到右扫描字符串然后移动,这就导致了一些字符被移动多次。可以换一个思路,从右到左扫描字符串。首先分配好替换后字符串的空间,如果原来的字符串为n,共s个空格字符,那么替换后的字符串长度为n+2*s。我们设置两个指针,一个指向替换前的字符串末尾p1,另一个指向替换后的字符串末尾p2。两个指针都从右向左移动,首先对当前p1指向的字符进行判断:若p1指向的字符c1不是空格,那么将c1复制到p2指向的位置,p2向左移动一位;如果p1指向的字符c1是空格,那么将p2以及p2的左两位填充为%20,p2向左移动2位。最后,将p1向左移动一位,重复此过程,直至p1将替换前的字符串从右到左遍历一遍。假设字符串长度为n,因为所有的字符只移动1次,所以时间复杂度为O(n)。
思路2对应的代码如下
class Solution {
public:
void replaceSpace(char *str,int length) {
if(str==nullptr || length==0)
return;
int strLen = 0;
int bsCnt = 0; //空格个数
for(int i=0; str[i]!='\0'; i++){
strLen++;
if(str[i]==' ')
bsCnt++;
}
int newStrLen = strLen + 2*bsCnt;
int cur1 = strLen; //替换前的字符串长度
int cur2 = newStrLen; //替换后的字符串长度
while(cur1>=0 && cur2>cur1){ //条件只写cur1>=0牛客网可以通过
if(str[cur1]==' '){
str[cur2--] = '0';
str[cur2--] = '2';
str[cur2--] = '%';
}
else str[cur2--] = str[cur1];
cur1--;
}
}
};
如果输入是 string 的话,这样写
class Solution {
public:
string replaceSpace(string s) {
if(s.empty()) return s;
int l1 = s.size();
for(int i=0; i<l1; i++){
if(s[i]==' ') s += "00"; // 注意是加两个零,不是3个
}
int l2 = s.size()-1;
for(int i=l1-1; i>=0; i--){
if(s[i]==' '){
s[l2--] = '0';
s[l2--] = '2';
s[l2--] = '%';
}else s[l2--] = s[i];
}
return s;
}
};
总结
在合并两个字符串(或者数组时),如果从左到右复制每个字符(或数字),则需要重复移动字符(或数字)多次。这种情况下,可以考虑从右向左移动字符,这样能减少移动的次数,从而提高效率。