标签:leetcode algorithm graph bfs python
如果固定一个行数(这里是3),字符串 "PAYPALISHIRING" 用“之”字形的方式可以写成:
P A H N A P L S I I G Y I R这个结果按照一行一行访问的话是:
"PAHNAPLSIIGYIR"。给定一个字符串以及一个表示行数的整数,实现一个函数,返回按行序拼接而成的新串。
string convert(string text, int nRows);
convert("PAYPALISHIRING", 3) 返回 "PAHNAPLSIIGYIR".
如果把字母的排列看成一个二维的表格,那么最左边的那些字母无疑落在了第0列上。题目要求按照行号从小到大、相同行号从左到右的方式组成新字符串。典型方法有两个:第一,用一个二维数组来存放,按照ZigZag的方式一列一列填充数组。然后按行访问就是新数组;第二,不适用额外空间,直接找出每一行不同字母在原字符串中的序号。leetcode上大多数方法应该都是这两种之一或其变体。
这里给出一个新思路,从另一个角度来看问题。如果把每个字母看成一个结点,字母之间的虚拟连接看成边,那么字符串就映射成了一张图。按照原始字符串中的排列顺序连接字母结点,这样每个字母(除了首、尾两个)都有前驱、后继两个邻居结点。反过来看,对于这样建立起来的一张图,原始序列其实就是对该图进行深度优先遍历(Depth First Search)的结果。
比如,对于字符串"ABCDE",ZigZag的排列方式如下,期望得到的新字符串为"AEBDC"。
A E B D C
按照上述思路建立出下图。唯一不同的是多了个根结点R。R的出现是为了后续的广度优先遍历(Breadth First Search)。 第0行的每个结点(如‘A‘, ‘E‘)都必须与R相连。
对这张图进行BFS,遍历序列为"RAEBDC",与期望结果相比只在最左端多了一个R,这可以通过为该结点赋一个空值""即可。下面说到的虚拟结点也可以采用相同技术。
在大功告成前还有最后一步,就是要处理一类特殊情况。之所以可以使用BFS来得到目标字符串序列的关键,是保证每个字母与源结点R有正确的距离。如果R的行号是-1,那么每个字母所在的行号(假设从0开始)与-1的距离就是距离源点R的深度。因为深度优先遍历是按照深度从小到大的顺序访问结点。当然,同一深度从左到右是我们这道题目的特殊要求,可以在建立图的时候得到保证。问题是E不一定存在。如原始串是"ABCD",对应的期望字符串为"ABDC"。但如果简单地把D与R相连的话就会产生"ADBC"的错误结果。一个方法是在最后一个处于非0行的字母与R之间建立虚拟结点,形成一条虚拟路径,如下图所示。
from collections import deque
class Node:
def __init__(self, value):
self.visited = False
self.value = value
self.neighbors = []
class Solution:
# @param {string} s
# @param {integer} numRows
# @return {string}
def convert(self, s, numRows):
self.__s = s
self.__numRows = numRows
return self.__BFS(self.__buildGraph())
def __connect(self, prev, this):
prev.neighbors.append(this)
this.neighbors.append(prev)
def __buildGraph(self):
'''Build the graph from DFS traversal of the string'''
root = Node('')
prev = None
row = 0
up = True
for i in range(len(self.__s)):
this = Node(self.__s[i])
if prev is not None:
self.__connect(prev, this)
prev = this
# Connect nearest nodes(those on row #0) to the root
if row == 0:
self.__connect(root, this)
if up:
if row < self.__numRows - 1:
row += 1
elif row > 0:
row -= 1
up = False
else:
if row > 0:
row -= 1
elif row < self.__numRows - 1:
row += 1
up = True
# The triky part, for BFS later
# Build a virtual path to connect to the root for the last branch, if not already
if not up and row < self.__numRows - 2:
for i in range(row, -1, -1):
this = Node('')
self.__connect(prev, this)
prev = this
self.__connect(prev, root)
return root
def __BFS(self, root):
'''Breadth First Search gives the desired string'''
work_queue = deque()
root.visited = True
work_queue.append(root)
s = ''
while work_queue:
node = work_queue.popleft()
# No special action for the root as it's an empty string;)
s += node.value
for i in node.neighbors:
if not i.visited:
i.visited = True
work_queue.append(i)
return s
这个基于图的深度优先遍历的方法可能没有其他常规方法快(时间复杂度都是O(n),n为字符串长度),但无疑是一种值得尝试的方法;)
版权声明:本文为博主原创文章,未经博主允许不得转载。
标签:leetcode algorithm graph bfs python
原文地址:http://blog.csdn.net/tonywearme/article/details/47719841