面试题4:替换空格之发散思维二
问题3.真的可以这样实现从后向前移动的算法吗?为什么?
从后向前替换的时候,因为替换后的数组实际长度大于原来的数组实际长度,所以要考虑到数组溢出的问题,首先需要找出字符串valueChars中包含几个oldChars,计算出替换后的字符串实际长度是否存在溢出,如果存在则返回,否则开始从后面开始替换。
问题就来了,从最后开始替换的时候,首先从最后一个字符开始比较,然后看是否相同,如果全部相同则代表这个词可以替换了。那么这样就和最先匹配原则出现的矛盾,例如'....aaaccc',oldChars='cc'时,newChars='ddd'时采用这种替换的方案,会替换后的字符串变为'....aaacddd',而根据这种最先匹配的原则,应该是'...aaadddc',这样就矛盾了!
要想解决这个问题,首先要遍历一次valueChars找到最后一个oldChars所在的位置,然后替换掉,重复执行这个步骤,实际的算法复杂度可能是>O(N*N)的,显然是不符合要求的。
答:不可以,因为这样找出来的oldChars可能不符合最先匹配原则,如果要求符合最先匹配原则,那么需要多次遍历valueChars,实际的算法复杂度实际的算法复杂度可能是>O(N*N)。
问题4.如果让你用比较小的内存开销,在时间复杂度上争取尽可能小,就是在内存和时间上找出平衡你有什么好的方案?
对于从前向后合并的情况,用原来的方案就很好,时间复杂度O(N*M),空间复杂度O(1)。
对于从后向前合并,由于以前要求内存O(1)的开销,导致我们在替换的时候需要不断的重复遍历valueChars,找到最后的一个oldChars,其实我们可以在第一次遍历的时候,就创建一个堆栈,记录每个oldChars的位置,然后替换他们。第一次遍历valueChars,是我们统计oldChars个数,防止替换后的串溢出的时候。因此可以写一个函数返回valueChars中所有oldChars的位置的堆栈。
详见代码:
2013年3月24日23:25:35
private Stack<int> IndexSubstring(char[] value, int vaLen, char[] subStr, int saLen) { if (saLen < 1 || vaLen < saLen) { return null; } Stack<int> indexStack = new Stack<int>(); int index = 0; //保证不会出现越界现象 while (index + saLen <= vaLen) { bool eq = true; for (int k = 0; k < saLen; k++) { if (value[index + k] != subStr[k]) { eq = false; break; } } if (eq) { indexStack.Push(index); index = index + saLen; } else { index++; } } return indexStack; }
得到子串在母串的所有位置之后就可以进行合并,合并的过程需要设置三个变量lastCopyLoc指针,指向原来的字符串,从后向前扫描时,已经处理的位置,index代表当前oldChars所在的位置,newVAlen新的字符串处理的位置,当堆栈为空时,结束复制。
问题5.当不限制内存时,可以为原来valueChars开辟新的内存空间时,有没有什么其他的方案?
答:由于不限制内存,我们完全可以新开辟一块儿足够大的char [],字符串的替换放到char []中进行,然后根据情况返回内存合适小的char[],
[ThreadStatic] static char[] mTempChars; protected static char[] GetTempData() { if (mTempChars == null) mTempChars = new char[1024 * 64]; return mTempChars; } private static int GetActualLength(char[] value) { int alen = 0; for (int i = 0; i < value.Length; i++) { if (value[i] == '\0') { break; } alen++; } return alen; } public static void CharsReplace(char[] value, char[] oldchars, char[] newChars) { int oAlen = GetActualLength(oldchars); if (oAlen < 1) return; int vAlen = GetActualLength(value); int nAlen = GetActualLength(newChars); char[] tmpchars = GetTempData(); int index = 0; int copyIndex = 0; bool eq = false; 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++) { tmpchars[copyIndex] = newChars[k]; copyIndex++; } index += oAlen; } else { tmpchars[copyIndex] = value[index]; index++; copyIndex++; } } while (index < vAlen) { tmpchars[copyIndex] = value[index]; index++; copyIndex++; } if (value.Length < copyIndex) { value = new char[copyIndex]; } for (int i = 0; i < copyIndex;i++ ) { value[i] = tmpchars[i]; } return; }
首先这个代码是参照http://www.cnblogs.com/smark/archive/2012/11/05/2754529.html 的代码(我在百度中搜索了半天,都没有找到最初的作者),我发现了里面有一个漏洞,(源代码的变量名和我的代码不一样)是while(index <vAlen){.....},这样会导致for循环中value[index+k]的部分越界,所以做了修改。
源代码中使用了[ThreadStatic]
属性,[ThreadStatic]来对每个线程分配一组Char[],因此这个在单例模式的时候,当线程访问时可以访问不同的tempChars的,可以避免数据不一致。
明天要把所有的代码结合起来组成一个新的类。
2013年3月25日23:02:44
菜包子 于宿舍