LeetCode 006 ZigZagConversion

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   N

A P L S I I G

Y   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]=4

A P L S I I G   …………………………………………  col[1]=7

Y   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 a(10) is okay
string 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 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 v alue(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个成员函数,详细见下图。
LeetCode 006 ZigZagConversion_第1张图片
(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 
#include 
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 the string length and cannot alter its content.
由上面描述可知,对于size,capacity的影响。
  • 对size,无法进行任何影响。
  • 对capacity,只增不减
string  a{"I Love You"};
a.reserve();        //no effect
a.reserve(0);       //no effect
a.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 the basic_string to reduce its capacity to fit its size .
This function has no effect on the string length and 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


你可能感兴趣的:(LeetCode,Leetcode,C++,string,ZigZag,capacity,and,size)