题目描述:
请实现一个函数,将一个字符串中的每个空格替换成“%20”。例如,当字符串为We Are Happy.则经过替换之后的字符串为We%20Are%20Happy。
解决思路:
题意很清楚,就是替换字符串,平常开发中会做的事情,我们通常会用replaceAll()来完成,显然也是行的通的。。
public class Solution {
public String replaceSpace(StringBuffer str) {
//排除特殊情况
if (str == null ) {
return null;
}
return str.toString().replaceAll(" ", "%20");
}
}
但显然题目并不希望你这样做,而是希望能够实现一个类似的过程。。
所以理想的解决思路应该是这样的:
首先采用StringBuffer(或者StringBuilder)这种缓冲区来提高性能完成整个替换过程,而不希望在原有字符串的基础上,新开辟一个字符串空间完成替换(String是不可变量)。其次我们通过遍历这个字符串,如果当前索引处的字符为“ ”时,就进行替换,否则不变,替换的过程相当于在当前空格位置插入“%20”这个3个字符,所以需要将该位置后的所有字符后移。这样就存在两个问题,(1)因为替换后字符串长度肯定增加了,所以StringBuffer需要扩容(我们手动扩容避免自动扩容带来的性能浪费)(2)实现字符后移的效果,我们是从前往后,化石从后往前?这里分析一下两者的区别,如果采用从前往后遍历,那么替换一个位置的字符,当前索引位置处的字符都要逐个向后移动一次,有多个位置需要替换,就会右多次重复的后移,而从后往前遍历的话,只需要将字符赋值到新位置上就行,相当于每个字符只需要移动一次即可,显然后者效率更高。下面一张图便于理解。。
所以确定了采用逆序遍历即可,我们判断原字符串中有几个空格,然后在进行扩容,“ ”替换成“%20”,每次替换多两个字符,所以扩容大小就是原长度+ 空格数*2。最后就是后移即可。
代码实现如下(Java版):
public class Solution {
public String replaceSpace(StringBuffer str) {
//排除特殊情况
if (str == null ) {
return null;
}
//统计原字符串长度
int oldLength = str.length();
//维护旧索引下标
int oldIndex = oldLength - 1;
//统计字符串中多少空格
int spaceNum = 0;
for (int i = 0; i < str.length(); i++) {
if (str.charAt(i) == ' ') {
spaceNum++;
}
}
//如果没有空格的话,无需替换,直接返回原字符串即可
if (spaceNum == 0) {
return str.toString();
}
//计算出空格替换后的字符串长度并扩容
int newLength = oldLength + spaceNum * 2;
int newIndex = newLength - 1;
str.setLength(newLength);
//从后往前顺序替换空格
char c ;
while (oldIndex >= 0) {
c = str.charAt(oldIndex);
if (c == ' ') {
//如果为空格替换
str.setCharAt(newIndex--, '0');
str.setCharAt(newIndex--, '2');
str.setCharAt(newIndex--, '%');
} else {
//否则赋予原值
str.setCharAt(newIndex--, c);
}
oldIndex--;
}
return str.toString();
}
}
这种方式时间复杂度为O(n),常数系忽略。