题目:将一个给定字符串 s
根据给定的行数 r ,以从上往下、从左到右进行 Z 字形排列。之后,你的输出需要从左往右逐行读取,产生出一个新的字符串。
当行数为1或行数不小于字符串长度时,直接返回字符串。
Z字形为周期性操作,每个周期均先向下存储r个数,然后向上存储r-2个数,所以每个周期字符数为t=2*r-2,每个周期的列数为c1=r-1。
一、粗暴方法(二维数组)
1、构建二维数组
行数为r,列数需要进行计算。
假设总共的周期数p。
假设字符串s的长度为len
若len%t==0,则p=len/t;
若len%t!=0,则p=len/t+1;
然后则可算出总共的列数c:
若len%t==0,则c=p*c1;
若len%t<=r,则c=p*c1+1;
否则,c=p*c1+1+len%t-r。
当然,也可以粗暴将列数固定为c=(p+1)*c1,避免上述多个判断,但是相应会增加内存。
2、存储数据
假设二维数组行索引为rw,列索引为col,遍历字符串s的索引为i。
若i%t<r-1,则表示数据向下存储,则rw++;
否则,表示数据向上存储,则rw--,col++。
3、循环按行读取数组
双重for循环读取即可,但是注意排除数组中的空字符。
代码如下:
public String convert(String s, int numRows) {
int len = s.length();
//若一行/行数不小于s的长度,则直接返回s
if (numRows==1||numRows>= len)
return s;
//首先计算需要多少列,
int col=0;
int perPeriod=2*numRows-2;//每个周期字符数为numRows+numRows-2
int remainder=len%(2*numRows-2);//余数
int quotient=len/(2*numRows-2);//商
if (remainder==0)
col=(numRows-1)*quotient;
else if (remainder<=numRows)
col=(numRows-1)*quotient+1;
else
col=(numRows-1)*quotient+1+remainder-numRows;
char[][] resultArray=new char[numRows][col];
int r=0;//实时行
int c=0;//实时列
for (int i = 0; i < len; i++) {
resultArray[r][c]=s.charAt(i);
if (i%perPeriod<numRows-1)
r++;
else {
r--;
c++;
}
}
StringBuilder result=new StringBuilder();
for (int i = 0; i < numRows; i++) {
for (int j = 0; j < col; j++) {
if (resultArray[i][j]!=0)
result.append(resultArray[i][j]);
}
}
return result.toString();
}
二、一维数组
我们可以将二维字符数组改为一维字符串数组,每行增加字符时只需要向对应行的字符串中附加即可。于是就有如下代码:
public String convertZ2(String s,int numRows){
int len = s.length();
//若一行/行数不小于s的长度,则直接返回s
if (numRows==1||numRows>= len)
return s;
int perPeriod=2*numRows-2;//每个周期字符数为numRows+numRows-2,每个周期的列数为numRows-1
int c=numRows-1;//同一列的标志
StringBuilder[] arr=new StringBuilder[numRows];
for (int i = 0; i < numRows; i++) {
arr[i]=new StringBuilder();
}
int r=0;//arr的实时行
for (int i = 0; i < len; i++) {
arr[r].append(s.charAt(i));
if(i%perPeriod<c){
r++;
}else
r--;
}
StringBuilder result=new StringBuilder();
for (StringBuilder str:
arr) {
result.append(str.toString());
}
return result.toString();
}
三、字符串直接存储并返回
仔细观察每个周期数据存储的位置,我们可以做如下总结:
假设行索引为i,周期的索引为j
第一行和最后一行的数据每个周期只会存储一个字符,均为i+j*t;
其余行每个周期均会存储两个字符,分别为i+j*t和t-i+j*t。
所以,我们可以直接遍历行和周期,得出每行需要存储得字符在字符串s中的位置并存储。
代码如下:
public String convertZ3(String s,int numRows){
int len = s.length();
//若一行/行数不小于s的长度,则直接返回s
if (numRows==1||numRows>= len)
return s;
int per=numRows*2-2;//每个周期的字符数
int num=len/per+(len%per==0?0:1);//总共几个周期
StringBuilder result=new StringBuilder();
for (int i = 0; i < numRows; i++) {
for (int j = 0; j < num; j++) {
if (i+j*per>len-1)
break;
result.append(s.charAt(i+j*per));
if (i!=0&&i!=numRows-1){
if(per-i+j*per>len-1)
break;
result.append(s.charAt(per-i+j*per));
}
}
}
return result.toString();
}