题目:请实现一个函数,吧字符串的每个空格替换成“%20”,例如输入“we are happy”,则输入“we%20are%20happy ”。
书中给出的代码是用C++实现的,时间复杂度是O(n),我的想法是用C#实现不指定字符串替换,一个字符串valueChars(char数组类型的),把字符串中的oldChars字符串,替换为newChars。注意valueChars,oldChars,newChars 三个的数据类型都是char [] 数组类型的。
开始写算法之前,想的算法的要求为:1,时间复杂度O(n) 2,常量的空间复杂度。实现过程中并不是这样的。
首先强调一点在C#中和C++中char[]是有一些区别的.
在C#中代码如下
char[] str = new char[10]; for (int i = 0; i < 10; i++) { if (5 <= i&&i <= 6) continue; str[i] = (char)(i+'0'); } Console.WriteLine(str);
请注意当str赋完值只收str的值,见下面图片
对于C#来说char[] 中的‘\0’并不是相当于C++中的字符串结束符,它可能不起作用,因此对于这点来说需要注意。
为了使算法具有普适性,valueChars的实际长度(从第一个字符开始,遇到第一个'\0'字符时的长度)和它的占用的内存长度不同,其他的两个字符串也是这样。
书中对于空格' '替换'%20',是从后面开始替换的,但是对于oldChars和newChars怎么办?最开始时我认为全部是从后开始替换就可以,但是当newChars的实际长度newActualLength(变量名nAlen)小于oldChars的实际长度(变量名oALen)时从后向前替换的问题时,已近替换好的字符串就有了一个需要向前移动的过程(自己脑补,不会画图)。这样显然不符合时间复杂度O(N)的要求,这个时候就需要向前开始移。
有缺陷的发散思维1:为了达到O(N)的时间复杂度(实际上不可能),当 nAlen<=oALen时,替换后字符串的实际长度是缩小或者不变的,这个时候就需要向前开始从前面开始替换。当nAlen>oALen时,我们应该从字符串后面开始替换字符串。
这样对应的算法是:
1.给定的oldChars和newChars 求出oALen和nAlen。
2.比较oALen和nAlen
3.nAlen<=oALen 调用ReplaceFromHead函数,跳转5否则4
4.调用ReplaceFromEnd函数
5.结束
按照这个思路,我就遇到了个各种各样的问题
问题1.如何认定字符串匹配的问题?
valueChars='aaabbbccc',oldChars='aa',newChars='d';出现了问题可以认为'aaa...' 和'aaa...'红色的部分是我要找的字符串oldChars,但是这两种情况替换下来结果完全不一样,因此采用最先匹配原则,即认为'aaa...'是应该的替换的字符串。
答:采用最先匹配原则
好了现在可以写从前向后匹配算法了,
问题2.可以实现算法的时间复杂度为O(N)吗?
答:不可能了,由于涉及到了字符串的匹配所以至少需要O(N*M)的时间复杂度,M是什么脑补,或者看下面的。
首先需要找到哪里有oldChars,请看代码
在母串中找子串的原则 bool eq=true; while (index + oAlen <= vAlen) { eq = true; for (int k = 0; k < oAlen; k++) { if (value[index + k] != oldchars[k]) { eq = false; break; } } if (eq) { //do some thing index = index + oAlen; } else { index++; } }
知道M是什么了吧?里面的度something就是实现替换oldChars和newChars的替换,while的条件一个是为了减少比较次数,还有就是保证for循环中的value[index+k]不越界,出现异常。
给出从前向后替换的全部代码,母字符串value,实际长度时vALen
public void ReplaceFromHead(char[] value, int vAlen, char[] oldchars, int oAlen, char[] newChars, int nAlen) { int newIndex=0, index = 0; bool eq = true; while (index + oAlen <= vAlen) { eq = true; for (int k = 0; k < oAlen; k++) { if (value[index + k] != oldchars[k]) { eq = false; break; } } if (eq) { for (int k = 0; k < nAlen; k++) { value[newIndex] = newChars[k]; newIndex++; } index = index + oAlen; } else { value[newIndex]=value[index]; newIndex++; index++; } } while (index<vAlen) {//把剩余的字符串移动到前面 value[newIndex] = value[index]; newIndex++; index++; } while(newIndex<value.Length) { value[newIndex] = '\0'; newIndex++; } }
newIndex代表移动后的value的指针,index扫描的是老的value的指针,当index扫描到新的oldChars时就替换,否则就把index指向的字符移到newindex的位置,然后一个while循环index后边的字符复制到index,完成字符串复制;在之后while保证新的value后面都是'\0',不会在输出是出错。
我们可以按照相同的思路完成从 后向前移动的算法。
问题3.真的可以这样实现从后向前移动的算法吗?为什么?
答:睡觉了,明天再说。
2013年3月24日0:36:55
菜包子 于宿舍