[LeetCode]#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".

 

二、解析

这题是一道找规律的数学题,找到规律后并不难。是要将原字符串按照Z字型排好后,从上到下以此输出元素。直接拿例子来说吧。
先说一下给的例子,写出Z字型的字母和下标,如下图所示。根据下标我们可以看到,其实无论字符串内容是什么,Z型的下标其实都是固定的。因此我们的任务转化成为:找到字符长度和Z层数之间的数学关系。也就是说,以3层为例,我们要输出的顺序是0 4 8 12 1 3 5 7 9 11 13 2 6 10这些位置上对应的字符。
P:0     A:4     H:8      N:12
A:1 P:3 L:5 S:7 I:9 I:11 G:13
Y:2     I:6     R:10

再画出一个5层的例子,方便对比:
0    8   16
1  7 9 15 17
2 6  10 14 18
3 5  11 13 19
4   12 20

 完整的Z字型是有规律可循的,比如5层中,后面所有的数字都在重复0-8的过程,所以我们来看看哪里可以被我们所用:

1. 3层中,第一行为0 4,5层中,第一行为0 8,这里我们发现,差值delta=2*(层数-1)。例如在5层中,第0行相同位置0和8相差8,第1行相同位置1 9相差8。好了,这里我们得打了第一个有用规则。继续往下看。

2. 3层中,最后一行为2 6 10,5层中,最后一行为4 12 20,相差为8上面已经得到。统筹的看第一行与最后一行,我们发现这两行的元素是比较整齐的,中间没有插入的元素。所有这两行在做处理的时候,应该归为一类。

3. 下面我们看最左边一行,都是0 1 2 3..代表的是第几层,ok这个很容易

4. 剩下最后的也是最复杂的,中间部分。拿5层中第1行为例:1 7 9 15 17,这里面其实有两部分内容。第一部分同第一行和最后一行一样,是比较整洁的元素,如1 9 17,他们之间相差都是8.第二部分就是多出来的7和15,如何求的他们呢?这时我们看第一行1和7相差6,第二行2和6相差4,第三行3和5相差2,这里我们就找到了规律,即:7=1+6=1+2*3, 6=2+4=2+2*2, 5=3+2=3+2*1。这里,等号左边是我们要通过公式得到的,等号右边第一项是行数,第二项2*n,n就是总行数-当前行数。7(要求的)=1(目前是第一行)+2*3(总行数为4,从0开始 - 1当前行)。

OK到这里,我们已经找到了所有的规律,总结如下:

1.相同位置差距delta为2*(层数-1)

2.第一行和最后一行无间隔元素

3.中间元素为:当前行+2*(总行数-当前层行)

 

三、代码:

 1 class Solution:
 2     # @param {string} s
 3     # @param {integer} numRows
 4     # @return {string}
 5     def convert(self, s, numRows):
 6         length = len(s)
 7         delta = 2 * (numRows - 1)
 8         zigzag_pos = []
 9         if numRows == 1 or length <= numRows:
10             return s
11         else:
12             #crate the zigzag pos to indicate the order of s
13             for i in range(numRows):
14                 for j in range(i, length, delta):
15                     zigzag_pos.append(j)
16                     if (i != 0) and (i != numRows - 1) and (j + (numRows - i - 1) * 2 < length):
17                         zigzag_pos.append(j+(numRows-i-1)*2)
18                     
19             zigzag_str = []
20             for i in zigzag_pos:
21                 zigzag_str.append(s[i])
22             return ''.join(zigzag_str)

这里解释一下else以后的那部分,是最主要的地方。

首先,我们用了两层循环i和j。i用来表示哪一层,j用来表示i层中的第几个元素。我们注意到在j循环中,用到了i, length, delta的写法,这样的意思是,从i开始,到length结束,每间隔delta是一个j。其实原本程序是这么写的,下面这个可能看的更清楚一些:

1 for i in range(numRows):
2     for j in range(i, length, delta):
3         if (i == 0) or (i == numRows - 1):
4             zigzag_pos.append(j)
6         else:
7             zigzag_pos.append(j)    
8             if (j + (numRows - i - 1) * 2 < length):
9                 zigzag_pos.append(j+(numRows-i-1)*2)
这个是不是更容易看懂些呢?
j循环中,第一个
if (i == 0) or (i == numRows - 1),是用来处理第一行和最后一行,比较干净的元素的。意思就是,这两行的元素,直接将j添加进list。
else就是中间元素。else后第一句zigzag_pos.append(j),是用来添加那些第一类元素,也是比较干净的,例如1 9 16这种
第二句if (j + (numRows - i - 1) * 2 < length),是用来记录中间元素,仔细看的话就是我们刚才推到出的公式。

由于if .. else ..中都有一句zigzag_pos.append(j),所以我将他提出来,改了一下判读条件,就成了第一份代码的样子。

三、总结
1.写代码之前一定要想清楚。像这种简单的数学题,一定要理清思路。不能靠乱改代码想着凑出一个正确答案,这样是很浪费时间的。
2.你们可能看到了我把第5题回文给跳过去了,只是因为耽误的时间有点多,想着先练一练手。另外我现在在放暑假哈哈哈,在家好幸福~~


posted @ 2015-07-21 16:59  面包包包包包包  阅读(113)  评论(0编辑  收藏  举报