题目大意:给定一个以之字形形式存储的字符串,并且给定之字形的行数,如下,行数为3,给定的string为"PAYPALISHIRING"
P A H N A P L S I I G Y I R然后现在一行一行的读取其值, 得到 "PAHNAPLSIIGYIR"
现在给定一个字符串s和行数numRows,输出其转换后的字符串。
首先考虑边界条件:s的长度小于行数,则直接输出s;numRows为1,也直接输出s。
思路:
1、对于行数为n时,可以看出每隔2n-2为一个周期ts,即上图P到A相差2n-2个。下面将以周期进行处理。
因此根据规律,第一行字符在s中的下标是一个等差数列,即0,2n-2,2*(2n-2),3*(2n-2),4*(2n-2)...
对于最后一行,在s中的下标也是一个等差数列,即(n-1),(n-1)+(2n-2),(n-1)+2*(2n-2),(n-1)+3(2n-2)...
2、对于中间行,因为在一个周期ts中出现了两次,而且出现的位置有一定的关系,设两次的位置分别为i1,i2,关系即为i1%ts+i2%ts=ts。
3、对于上述两点,可以归并到一起处理。当求出的i1==i2时,此时即为第一行和最后一行情况。
下面是ac的代码,运行时间是16ms:
class Solution {
public:
string convert(string s, int numRows) {
int m=0,i1=0,i2=0,j=0;
string resu;
if(s.length()<=numRows)
return s;
if(numRows==1)
return s;
int ts=(2*numRows-2);
for(m=0;m<numRows;++m)
{
j=0;
i1=j+m%ts;
i2=j+(ts-m)%ts;
while(i2<s.length())
{
if(i1!=i2)
{
resu+=s[i1];
resu+=s[i2];
}
else
resu+=s[i1];
j+=ts;
i1=j+m%ts;
i2=j+(ts-m)%ts;
}
if(i1<s.length())
resu+=s[i1];
}
return resu;
}
};
此算法是O(n)的,外层执行次数为行数numRows,每次while需访问每一行的每个元素一次,总次数为s.length(),因此复杂度为O(n)。因此代码的运行时间,已经是最低了。并且没有使用大量的空间,唯一的缺点是,有点难于理解,不够简洁。
下面是讨论区的最简单的实现,用了字符串数组来记录每行的字符,最后再合并起来:
string convert(string s, int nRows) {
if (nRows <= 1)
return s;
const int len = (int)s.length();
string *str = new string[nRows];
int row = 0, step = 1;
for (int i = 0; i < len; ++i)
{
str[row].push_back(s[i]);
if (row == 0)
step = 1;
else if (row == nRows - 1)
step = -1;
row += step;
}
s.clear();
for (int j = 0; j < nRows; ++j)
{
s.append(str[j]);
}
delete[] str;
return s;
}
2016.09.04更新 JAVA代码:
public class Solution {
public String convert(String s, int numRows) {
if(s == null || numRows < 2){
return s;
}
char[] str = new char[s.length()];
int len = s.length();
int index = 0;
for(int i = 0; i < numRows; i++) {
for(int j = i; j < len; j += (2 * numRows - 2) ) {
str[index++] = s.charAt(j);
int mid = j + (2 * numRows - 2 -2 *i);
if(i != 0 && mid > j && mid < len) {
str[index++] = s.charAt(mid);
}
}
}
return String.valueOf(str);
}
}