题目:
请实现一个函数,将一个字符串中的每个空格替换成“%20”。例如,当字符串为We Are Happy.则经过替换之后的字符串为We%20Are%20Happy。
这道题其实不算难,如果是可以额外开辟新的字符串来存储修改后的字符串的话等于没难度,但是本题的要求是直接修改原始的字符串,这样就对算法提出了一些要求。题目给出的函数参数是字符串str和长度length,需要注意这里的length指的不是str含有字符的长度,而是含有剩余空间的长度(不然就没法扩容了)。
直接修改原始字符串有两种做法,一种是双重循环遍历整个字符串,外层循环是遍历字符串、内层循环是遇到空格后把当前位置以后的所有字符串都向后挪,这样做的时间复杂度大约是O(n^2)。其实我也想到了从后向前的想法,和最佳解法一样也是先求出整个字符串中的空格数count,然后从后向前遍历字符串,但是采用的方法比较麻烦:在没有遇到空格的时候就把当前字符往后挪count*2个,遇到一个空格就count--。在看了剑指的解析以后才突然想起了快慢指针,前段时间刷lc也遇到过的但是现在全都忘了。直接设置两个指针,一个p2指向未来扩容后的尾部,一个p1指向当前遍历的尾部,在非空格时只需要将p1复制到p2的位置就好,在空格时调整p2。算法其实很简单,但是写起来其实有一些需要注意的小细节(准确来说是我自己的知识盲区):
1. 字符串的最后是带一个'\0'的,在第一次遍历字符串求原始长度的时候计算出来的长度是不包括'\0'的,因此在设置快慢指针的时候需要注意,由于也需要把'\0'从原始位置移动到新位置,所以指针的位置不需要-1
2. 设置指针内容和移动指针时,可以采用自增减来简化代码,比如str[a++]的操作顺序是先对str[a]进行操作,然后才执行a = a + 1
代码如下,时间复杂度O(n),空间复杂度O(1):
class Solution {
public:
void replaceSpace(char *str,int length) {
if (str == NULL || length <= 0) {
return;
}
int old_len = 0;
int count = 0;
int i = 0;
while (str[i] != '\0') {
old_len++;
if (str[i] == ' ') {
count++;
}
i++;
}
int new_len = old_len + count * 2;
if (new_len > length) {
return;
}
int p1 = old_len; //这里不需要-1是因为最后还有个隐藏的'/0'
int p2 = new_len;
while (p1 >= 0) {
if (str[p1] != ' ') {
str[p2--] = str[p1]; // 先运行str[p2] = str[p1],然后再执行p2--
}
else {
str[p2--] = '0';
str[p2--] = '2';
str[p2--] = '%';
}
p1--;
}
}
};