我认为所写的问题应解释如下:
给定一个字符串,并希望将该字符串作为螺旋线写入方形网格。编写一个函数,找到可以容纳字符串的最小方格,将字符串按顺时针方向围绕网格旋转,将字符串写入网格,最后将行连接在一起。
例如,字符串“In a spiral”看起来像这样:
I N A
In a spiral -> A L S -> INAALSRIP
R I P
要查看网格的来源,请注意,如果您是这样阅读的:
I -> N -> A
|
v
A -> L S
^ |
| v
R <- I <- P
您会返回初始文本,如果将行“INA”、“ALS”和“RIP”粘贴到单个字符串中,则会返回“INAALSRIP”。
让我们分别考虑每个问题。首先,要查看可以容纳文本的矩形的最小尺寸,您实际上是在寻找至少与文本长度一样大的最小完美正方形。要找到这个,您可以取字符串长度的平方根并将其四舍五入到最接近的整数。这为您提供了您想要的尺寸。但是,在您这样做之前,您需要从字符串中删除所有标点符号和空格字符(可能还有数字,具体取决于应用程序)。您可以通过遍历字符串并将确实是字母的字符复制到新缓冲区中来做到这一点。在接下来的内容中,我假设您已经完成了这项工作。
至于如何实际填充网格,有一个非常棒的方法可以做到这一点。直觉如下。当您从 n x n 网格开始时,您唯一的边界是网格的墙壁。每次你穿过网格放下字母并撞到墙上时,你只是从矩阵中刮掉了一行或一列。因此,您的算法可以通过跟踪第一个和最后一个合法列以及第一个和最后一个合法行来工作。然后,您从左到右走过顶行书写字符。完成后,然后增加第一个合法行,因为你不能再放任何东西了。然后,沿着右侧向下走,直到到达底部。完成后,您也可以将最后一列排除在外。例如,回顾我们的“螺旋形”示例,我们从一个空的 3x3 网格开始:
. . .
. . .
. . .
在顶部写下前三个字符后,剩下的是:
I N A
. . .
. . .
现在,我们需要将字符串的其余部分写入空白区域,从右上角开始向下移动。因为我们永远无法写回第一行,所以一种思考方式是考虑解决在较小的空间中以螺旋形式书写其余字符的问题
. . .
. . .
从左上角开始向下移动。
要真正将其实现为一种算法,我们需要在每个点跟踪一些事情。首先,我们需要在更新它们时存储世界的边界。我们还需要存储我们当前的写入位置,以及我们面临的方向。在伪代码中,这表示如下:
firstRow = 0, lastRow = N - 1 // Bounds of the grid
firstCol = 0, lastCol = N - 1
dRow = 0 // Amount to move in the Y direction
dCol = 1 // Amount to move in the X direction
row = 0 // Current position
col = 0
for each character ch in the string:
Write character ch to position (row, col).
// See if we're blocked and need to turn.
If (row + dRow, col + dCol) is not contained in the rectangle [firstRow, lastRow] x [firstCol, lastCol]:
// Based on which way we are currently facing, adjust the bounds of the world.
If moving left, increment firstRow
If moving down, decrement lastCol
If moving right, decrement lastRow
If moving up, increment firstCol
Rotate 90 degrees
// Finally, move forward a step.
row += dRow
col += dCol
您可以使用线性代数中的一个技巧来实现 90 度转弯:将向量向左旋转 90 度,将其乘以 rotation matrix
| 0 1 |
| -1 0 |
所以你的新 dy 和 dx 由
给出
|dCol'| = | 0 1 | dCol = |-dRow|
|dRow'| | -1 0 | dRow | dCol|
所以你可以通过计算左转
temp = dCol;
dCol = -dRow;
dRow = temp;
另外,如果您知道数值为零的字符永远不会出现在字符串中,您可以使用 Java 初始化所有数组以在任何地方都保持零的事实。然后,您可以将 0 视为哨兵,意思是“继续前进是安全的”。该版本的(伪)代码如下所示:
dRow = 0 // Amount to move in the X direction
dCol = 1 // Amount to move in the Y direction
row = 0 // Current position
col = 0
for each character ch in the string:
Write character ch to position (row, col).
If (row + dRow, col + dCol) is not contained in the rectangle [0, 0] x [n-1, n-1]
-or-
The character at [row + dRow, col + dCol] is not zero:
Rotate 90 degrees
// Move forward a step
row += dRow
col += dCol
最后,一旦您将字符串写入螺旋,您可以通过一次一行地遍历行并将找到的所有字符连接在一起,将螺旋文本转换回字符串。
编辑:正如@Voo 指出的那样,您可以通过根本不实际创建多维数组而是将多维数组编码为一维数组来简化该算法的最后一步。这是一个常见的(而且很聪明!)技巧。例如,假设我们有一个这样的网格:
0 1 2
3 4 5
6 7 8
那么我们可以用一维数组来表示这个
0 1 2 3 4 5 6 7 8
这个想法是,给定 N x N 网格中的 (row, col) 对,我们可以通过查看位置 row * N + col 将该坐标转换为线性化数组中的相应位置。直观地说,这表示你在 y 方向上的每一步都相当于跳过一行中的所有 N 个元素,并且每一个水平步骤只是在线性化表示中水平移动一个步骤。
希望这会有所帮助!