[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题回文给跳过去了,只是因为耽误的时间有点多,想着先练一练手。另外我现在在放暑假哈哈哈,在家好幸福~~