LeetCode 006 ZigZagConversion

来源:互联网 发布:图片热点 js 边框 编辑:程序博客网 时间:2024/04/30 02:53
6. ZigZag Conversion

The string "PAYPALISHIRING" is written in a zigzag pattern on a given number of rows like this: (you may want to display this pattern in a fixed font for better legibility)

P   A   H   NA P L S I I GY   I   R

And then read line by line: "PAHNAPLSIIGYIR"

Write the code that will take a string and make this conversion given a number of rows:

string convert(string text, int nRows);

convert("PAYPALISHIRING", 3) should return "PAHNAPLSIIGYIR".

class Solution {public:    string convert(string s, int numRows) {    }};

解题思路:
  • 自己的解题思路
一开始认为题目很简单,但是实际操作起来,发现自己的思路实现起来并没有那么简单。似乎这种感觉经常会出现。所以,不能眼高手低,一定要实现出来,绝不能想当然。
思路:
首先,将zigzag图案的每行的列数算出来;如下,
P   A   H   N   …………………………………………  col[0]=4A P L S I I G   …………………………………………  col[1]=7Y   I   R       …………………………………………  col[2]=3


然后计算结果res;依次从每行开始遍历,然后对于每行的每一列进行遍历,运用相应的行列对应公式就可以得出结果。
总评:对应公式比较复杂,期间调试很久。这真是一个糟糕的算法。
  • 别人的解题思路
[1]   第一个方法很巧妙,它将每一个ZigZag图案拉长,变成锯齿形图案。
P         A           H             N  A    P     L     S     I       I       G    Y           I              R


利用辅助数组string[nRows],记录每行的string,然后链接就可以得到结果。
好处,不需要复杂的源字符串与目的字符串的对应公式。
[2]   第二个方法,跟我的思路比较靠近,但是它的思路更清晰。
首先对应一个cycle=2*nRows – 2;这相当于步长,但是出去首行跟末行,其他行还有中间一个元素需要对应,因此在循环中还需要一个判断条件。
学习收获:
  • 边界条件(Boundary Conditions)加深理解
自己写的程序,一开始没有考虑边界条件。总是,直接提交,然后再看通不过的测试案例,然后再修改。虽然最后也能通过,但是在CCF认证考试,浙大的PAT考试,找工作的机试都是只能提交代码的。偶尔的系统还会告诉你,这个程序有没有全部通过测试案例,但是几乎全不会像LeetCode这么人性化还给你错误的案例。因此,必须对此加以重视,否则要找不到工作的节奏。
总结一下:这次犯的错误。
边界条件,主要就是对输入的把控。
  • 如果程序中,有除法运算,一定要记得考虑是否除数为0的情况。
  • 对于string,考虑size()为0的情况。对于int,考虑为0  以及正负数的情况。
本题,没有考虑到负数的情况,说明我的程序还是有漏洞的,需要重视。别人的程序就有考虑。
  • 对于string部分的初始化、size以及capacity理解更深。
string a(10);            //no exist such initialization    but vector<int> a(10) is okaystring a(10,’\0’)      //but you can do like this


对于一个string a;需要时刻考虑他的size以及capacity。
eg:    string a;
       a.reserve(10);   //PS:reserve()的效果是只增不减
       a[6]=’x’;    //就是会报错的,reserve只是改变了capacity,而不是size
对于string的其他相关知识,可以参考附件2。
  • Word技巧:如何左右观看同一个文档,以及上下观看同一个文档。
【左右】点视图->新建窗口。 之后,会出现同一个Word文档的窗口。特点,一个窗口改变,一个窗口的Word也会同步改变。
之后,再点->并排查看  默认的情况是:同步滚动的,意思就是向下拉一个窗口,另一个窗口也在往下拉。而一般是不需要这个并排查看的,再点击一下,就可以去除。
之后,就是可以完美左右查看文档。
关闭方法:直接关闭一个窗口就可以。
【上下】点视图->拆分;就可以上下观看同一个文档的两个不同部分。
关闭方法,再次点击拆分,即可。
附件:程序
1、自己的程序:
string convert(string s, int numRows)    {        //boundary conditions: (1) s.size()==0; (2) numRows == 1        //what about other impossible instances   eg:numRows == 0        if(s.size() == 0)        {            return string();        }        else if(numRows == 1)        {            return string(s.begin(), s.end());        }        //initialize res, but we cannot do like this: string res(sz);        int sz = s.size();        string res(sz, '\0');                //col[] is to store every row's length        vector<int> col(numRows, 0);        //compute col[0]        col[0] = (sz - 1) / ((numRows << 1) - 2) + 1;        //compute col[ 1, 2, ..., numRows-2 ]        for(int i = 1; i < numRows - 1; ++i)        {            if(sz - ((numRows - 1)*((col[0] - 1) << 1) + 1 + i) < 0)            {                col[i] = (col[0] - 1) << 1;            }            else if(sz - ((numRows - 1)*((col[0] - 1) << 1) +(numRows << 1) - i - 1) < 0)            {                col[i] = (col[0] << 1) - 1;            }            else            {                col[i] = col[0] << 1;            }        }        //compute col[numRows-1]        if(sz - ((numRows - 1)*((col[0] - 1) << 1) + numRows) < 0)        {            col[numRows - 1] = col[0] - 1;        }        else        {            col[numRows - 1] = col[0];        }        //to get the res        for(int index = 0, i = 0; i < numRows&&index != sz; ++i)        {            if(i == 0 || i == numRows - 1)            {                for(int j = 0; j != col[i]; ++j)                {                    res[index++] = s[(((numRows - 1)*j) << 1) + i];                }            }            else            {                for(int j = 0; j != col[i]; ++j)                {                    if(j % 2 == 0)                    {                        res[index++] = s[(j >> 1)*((numRows - 1) << 1) + i];                    }                    else                    {                        res[index++] =s[(j >> 1)*((numRows - 1) << 1) + (numRows << 1) - i - 2];                    }                }            }        }        return res;    }


2、别人的程序
string convert(string s, int nRows)    {        if(nRows <= 1)            return s;        const int len = (int)s.length();        string *str = new string[nRows];        int temp = (len - 1) / ((nRows << 1) - 2) + 1;        int limits = temp << 1;        for(int i = 0; i < nRows; ++i)        {            str[i].reserve(limits);        }        int row = 0, step = 1;        for(int i = 0; i < len; ++i)        {            str[row].push_back(s[i]);            if(row == 0)                step = 1;            else if(row == nRows - 1)                step = -1;            row += step;        }        s.clear();        for(int j = 0; j < nRows; ++j)        {            s.append(str[j]);        }        delete[] str;        return s;    }string convert(string s, int nRows)    {        if(nRows <= 1) return s;        string result = "";        //the size of a cycle(period)        int cycle = 2 * nRows - 2;        for(int i = 0; i < nRows; ++i)        {            for(int j = i; j < s.length(); j = j + cycle)            {                result = result + s[j];                //j-i 回跳到之前尖点[行号为0],   +cycle 则是跳到下一个尖点                  //-i  则是回跳到前面的第i行    思路很清晰                int secondJ = (j - i) + cycle - i;                if(i != 0 && i != nRows - 1 && secondJ < s.length())                    result = result + s[secondJ];            }        }        return  result;    }


附件2:扩展阅读(转载)
  1. C++中string的size,length,capacity三者到底有何区别求解.https://zhidao.baidu.com/question/1048376565616057099.html.
测试发现
1.    std::string value(2, ‘a’);
       结果:   value.size() == value.length()==2;  value.capacity()==31
      std::string value(31, ‘a’);
       结果:   value.size() == value.length()==value.capacity()==31;
 
2.    std::string value(32, ‘a’);
       结果:   value.size() == value.length()==32;   value.capacity()==63;
       std::string value(63, ‘a’);
       结果:   value.size() == value.length()==value.capacity()==63;
 
3.     std::string value(80, ‘a’);
       结果:   value.size()==value.length()==80;       value.capacity()==95;
        std::string value(95, ‘a’);
        结果:  value.size()==value.length()==value.capactiy()==95;
 
举这3个例子不难发现
    a) .   size() 和 length() 效果一样,不过C++的话,倾向于用 size();
    b) .   string的容量,也就是capactiy(),如果 value值为空,则capactiy()==0;
         否则,capacity() 初始值为32,根据string 存储的量的变化而变化
         初始值=31,步长=32;  
【原创】点评
当然,这个各个机器不一样,比如有初始值=15,步长=16。至于为什么31,而不是32,因为\0原因。字符串前后都有\0,但是无法利用*(s.end());*(s.rend())取出,可以用operator[]查看。

  1. C++容器中 size(), capacity, reserve() ,resize() 函数讲解.http://blog.csdn.net/youxin2012/article/details/9213539

【原创】点评
关于size和capacity,以及reserve()讲解的不错,遗憾的是对于resize()没有结合实例,进行深入讲解。

附件3:扩展阅读(自我总结)
好好总结一下size,capacity
至于size(),capacity()的大小关系,以及相应的编译器的分配原则,上面的参考资料已经讲解的很清楚。我这里主要介绍关于capacity,size(等价于length,string为了统一性所以加入size()。由于length()之前就有,加上名字也很合适,造成了一些冗余尴尬)。
由于string类里面关于capacity的相关操作比较全,我们就以capacity进行总结。主要涉及9个成员函数,详细见下图。
(1)首先,介绍有return值得几个成员函数。

size(),length()       //等价,返回字符串大小;   capacity()            //返回分配的内存空间的大小。这个不是指分配给对象string s的大小,而是对象里面有个指针,也就是s.c_str()所指向的对象所获得内存空间max_size()            //值就是npos-1 为什么不是npos?可能是为了存放\0吧。   可能值4294967294,不同编译器值可能不一样    这个无需深究

(2)接下来,来看看与size直接相关的成员函数

resize()    void resize (size_type n);              //n小于目前的size,则截断;如果大于,则使用\0进行初始化多出来的元素void resize (size_type n, charT c);    //n小于目前的size,则截断;如果大于,则使用c进行初始化多出来的元素


两个函数原型。
对于size,capacity的影响。
  • 对size,直接进行影响,完全控制size的取值。
  • 对capacity,如果size变小,没有任何影响;如果size变大,可能会引起capacity的改变。
PS:   resize()常用来快速初始化数组,尤其是二维数组。
相关测试程序如下,下面几个成员函数,只要在这个程序的基础上,加加减减就行,所以就不再贴出来。
自己可以跑一下,改改数据。

#include <iostream>#include <string>using namespace std;int main(){    std::string str(200,'\0');    std::cout << "size1: " << str.size() << "\n";    std::cout << "length1: " << str.length() << "\n";    std::cout << "capacity1: " << str.capacity() << "\n";    std::cout << "max_size1: " << str.max_size() << "\n";    str.resize(3);    std::cout << "size2: " << str.size() << "\n";    std::cout << "capacity2: " << str.capacity() << "\n";    std::cout << std::string::npos << std::endl;    return 0;}



(3)接下来,继续看看与capacity直接相关的几个成员函数

reserve()
void reserve (size_type n = 0);
This function has no effect on thestring lengthand cannot alter its content.
由上面描述可知,对于size,capacity的影响。
  • 对size,无法进行任何影响。
  • 对capacity,只增不减
string  a{"I Love You"};a.reserve();        //no effecta.reserve(0);       //no effecta.reserve(100);     //size no change;  capacity rise to more than 100 and nearest to 100  就是大于100也是最靠近100的capacity 我的VS结果是111



PS:由于push_back(),超过capacity时,会出现销毁旧内存,重新申请内存等耗时操作。因此,对于多次的push_back(),可以提前指定capacity,这样可以节省运行时间。该技巧在leetcode上面,对于提升性能,效果明显。
void shrink_to_fit();   C++11
Requests thebasic_stringto reduce itscapacityto fit itssize.
This function has no effect on thestring lengthand cannot alter its content.

由于reserve()对capacity的限制,且shrink_to_fit无参数传入,可是理解shrink_to_fit对于capacity只减不增。
由上面描述可知,对于size,capacity的影响。
  • 对size,无法进行任何影响。
  • 对capacity,只减不增。但不是减到与size相等,而是大于size,且最近size的capacity。
因此,可以得出结论,capacity的值都是系统指定的,我们无法直接控制,但是可以间接控制范围。

(4)最后,介绍几个相关的操作

bool empty() const;        //看的是size,而不是capacity。由于是const函数,所以不改变size跟capacity.void clear();       //size to 0;  capacity no change


参考资料:       cplusplus.com

Constructive comments and reports of errors are always welcome.
Written by Josan.
2016/12/19


0 0
原创粉丝点击