• 面试题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

    返回母串中子串位置的堆栈,从母串0的位置开始扫描
            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

                                                        菜包子   于宿舍

  • 相关阅读:
    app保存图片
    字母数字换行css
    module.exports 和 exports,export 和export default的区别
    cs滚动条样式
    css画三角形、带边框的三角形、气泡三角形
    js将一位数组分割成每6个一组
    CSS DROP SHADOWS WITHOUT IMAGES
    QTableWidget焦点方式循环焦点从而导致无法把焦点切到其它控件
    QT Widget focus样式
    qt QButtonGroup里面的button,设置非checkde状态,而不是通过设置其他按钮的形式
  • 原文地址:https://www.cnblogs.com/CaiBaoZi/p/2979542.html
Copyright © 2020-2023  润新知