首先看一下题目
问题描述:
给定一个字符串,及一个整数表示把其分成几行(Z字长度),然后依次竖着画Z字。
这个问题其实很好解决,根据题目意思,如果需要n行,建立n个数组指针,然后按规则一个一个赋值,最后老老实实地把字符数组整合成一个字符串即可。但是这种算法明显无脑,且浪费空间,代码量想想也知道会很冗余。
于是我尝试画了一个5行,字符串长度为21的Z字形图:
这里我只是罗列了每个字符在字符串中的下标值。所得结果十分乐观,隐隐给我一种可以直接按照某种跳跃读取规则来完成对源字符串的不断提取以获得目标字符串的可能性。
其实仔细观察,多画几个图就可以发现确实有规律可循(这是Z字带来的特性),假定给定行数为n(例如上图为5),那么就会有以下特性:
- 首行和尾行两个数据之间的距离都是distanceForHeadAndTail = n * 2 – 2(如上图为8),因为它实际是经历了一个n行到最底部后又要经历一个n行爬到第一行,期间距离还要减去两个首行(或两个尾行)本身
- 中间部分每一行的数据间隔是周期变化的,如第i行(如上图第1行),它的间隔分别是distanceForBody1 = distanceForHeadAndTail – 2 * i(如6)和dosyamceForBody2 = distanceForHeadAndTail – 2 * (n – i – 1)(如2),因为对于Z字,下降和上升的路线不一样,一个是竖直下降,一个是斜着下降,因此间隔也会不同。
找到规律后我们就可以开始着手编写代码了。首先我们排除只有一行的,因为这个不需要操作直接返回字符串即可
然后我们对每一行进行读取,对第一行、中间行进行不同的读取规律:
对于第一行,最后一行,直接加上distanceForHeadAndTail来计算下一个读取位置:
对于中间行,用T来表示时间间隔来计算下一个读取位置:
总体代码如下:
string convert(string text, int nRows) {
if (nRows == 1)
return text;
string answer;
int NowRow = 0;
while (NowRow != nRows) {
int distanceForHeadAndTail = 2 * nRows - 2;
int pos = NowRow;
int T = -1;
int distanceForBody1 = distanceForHeadAndTail - 2 * NowRow;
int distanceForBody2 = distanceForHeadAndTail - 2 * (nRows - NowRow - 1);
while (pos < text.size()) {
answer += text[pos];
// 如果当前行是第0行或尾行,那么两个数据之间的间隔是distanceForHeadAndTail
if (NowRow == 0 || NowRow == nRows - 1) {
pos += distanceForHeadAndTail;
}
else {
// 当前行是内部行,两个数据之间的间隔是以周期为1来改变的,即这一次是distanceForBody1,那么下一次就是distanceForBody2
if (T == -1)
pos += distanceForBody1;
else
pos += distanceForBody2;
// 时钟向后走一次(用于记录周期)
T = -T;
}
}
NowRow++;
}
return answer;
}
算法涵盖嵌套循环,时间复杂度为O(n²),但是都是建立在对给定字符串的读取上的,因此我认为还是比较不错的算法。