题目:
请实现一个函数,把字符串中的每个空格替换成“%20”。例如输入“We are happy.”,则输出“We%20are%20happy.”。
第一思路:
(1):是在原来的字符串上做替换(一定要保证内存够用)还是在新创建的字符串并在新的字符串上做替换。因为“ ”替换成“%20”字符串的长度会增加;
(2):最直观的做法是O(n^2)的解法;从头到尾扫描字符串,每一次碰到空格的时候做替换。由于是把一个字符替换成3个字符,我们必须要把后面的所有字符都后移两个字节,否则就有两个字符被覆盖了。故假设字符串的长度是n,对每个空格字符,需要移动后面O(n)个字符,因此对含有O(n)个空格的字符串而言总的时间效率是O(n^2)。
最优解思路:
上面的做法会使时间复杂度达到O(n^2),于是需要提升上面的解题思路使其时间复杂度达到O(n^2);先遍历一遍字符串统计出字符串中空格的总长度,因每次替换一个空格长度+2;故所需的总长度为oldLenth + blankCount * 2;上次我们的时间复杂度达到n的平方,是因为每次替换一个空格,我们的后面的字符串都要移动造成的,故这次我们从后往前移动,使字符在第一次就到达自己的位置上,故这样做的话时间复杂度为O(n);
代码:
public static String replaceBlank(StringBuffer sb){
int blankCount = 0;
//先统计出空格的数量
for (int i = 0; i < sb.length(); i++) {
if(sb.charAt(i)== ' '){
++blankCount;
}
}
int oldLength = sb.length(); //得到原来字符串的长度
int newLength = oldLength + blankCount * 2; //将空格替换为"%20"后的字符长度
int oldIndex = oldLength - 1; //原来字符串的索引
int newIndex = newLength - 1; //新字符串的索引
sb.setLength(newLength);
//一次遍历,替换其中的空格为"%20"
while(oldIndex >= 0 && newIndex > oldIndex){
if(sb.charAt(oldIndex)== ' '){
sb.setCharAt(newIndex--, '0');
sb.setCharAt(newIndex--, '2');
sb.setCharAt(newIndex--, '%');
}else{
sb.setCharAt(newIndex--, sb.charAt(oldIndex));
}
--oldIndex;
}
return sb.toString();
}
第二种解法:
利用Java自己的函数replace(param, param, param)函数帮助我们替换字符串。我们只需要找到空格的位置即可
public static String replaceBlank(StringBuffer sb){
for (int i = 0; i < sb.length(); i++) {
char ch1 = sb.charAt(i);
if(ch1 == ' '){
sb.replace(i, i+1, "%20");
}
}
return sb.toString();
}
replace函数的源码:其中count是计算str的长度
/**
* Replaces the characters in a substring of this sequence
* with characters in the specified <code>String</code>. The substring
* begins at the specified <code>start</code> and extends to the character
* at index <code>end - 1</code> or to the end of the
* sequence if no such character exists. First the
* characters in the substring are removed and then the specified
* <code>String</code> is inserted at <code>start</code>. (This
* sequence will be lengthened to accommodate the
* specified String if necessary.)
*
* @param start The beginning index, inclusive.
* @param end The ending index, exclusive.
* @param str String that will replace previous contents.
* @return This object.
* @throws StringIndexOutOfBoundsException if <code>start</code>
* is negative, greater than <code>length()</code>, or
* greater than <code>end</code>.
*/
public AbstractStringBuilder replace(int start, int end, String str) {
if (start < 0)
throw new StringIndexOutOfBoundsException(start);
if (start > count)
throw new StringIndexOutOfBoundsException("start > length()");
if (start > end)
throw new StringIndexOutOfBoundsException("start > end");
if (end > count)
end = count;
int len = str.length();
int newCount = count + len - (end - start);
ensureCapacityInternal(newCount);
System.arraycopy(value, end, value, start + len, count - end);
str.getChars(value, start);
count = newCount;
return this;
}
第三种解法:利用Java已经封装好的函数
public static String replaceBlank2(String str){
String str1 = str.replaceAll(" ", "%20");
return str1;
}
replaceAll函数实现
public String replaceAll(String regex, String replacement) {
return Pattern.compile(regex).matcher(this).replaceAll(replacement);
}
测试:
public static void main(String[] args) {
StringBuffer sb = new StringBuffer();
sb.append("We are happy.");
String str = replaceBlank1(sb);
System.out.println(str);
}
总结:
这道编程题考察对字符串的编程能力;
考察分析时间效率的能力,知道时间复杂度的计算;
对内存的考察;
课后小思:
有两个排序的数组A1和A2,内存在A1的尾部有足够多的空间去容纳A2。实现一个函数是A2插入A1是有序的,且时间复杂度最小。最后解的思路还是从尾部开始比较A1和A2,并把较大的数字复制到A1的合适位置,即第一次都到达自己的最终位置。故这类题目合并两个数组,如果从前往后复制每个数字需要重复移动数字多次,那么我们可以考虑从后往前复制,这样时间复杂度就会大大降低。