算法,不如说它是一种思考方式
算法专栏: 123
明明自觉学会了不少知识,可真正开始做题时,却还是出现了“一支笔,一双手,一道力扣(Leetcode)做一宿”的窘境?你是否也有过这样的经历,题型不算很难,看题解也能弄明白,可一到自己做就变成了与题面面相觑无从下手?
numRows
,以从上往下、从左到右进行 Z 字形排列。比如输入字符串为 “PAYPALISHIRING” 行数为 3 时,排列如下:
P A H N
A P L S I I G
Y I R
之后,你的输出需要从左往右逐行读取,产生出一个新的字符串,比如:“PAHNAPLSIIGYIR”。
请你实现这个将字符串进行指定行数变换的函数:
string convert(string s, int numRows);
来源:力扣(LeetCode)
难度:中等
提示:
1 <= s.length <= 1000
s 由英文字母(小写和大写)、‘,’ 和 ‘.’ 组成
1 <= numRows <= 1000
示例 1:
输入:s = “PAYPALISHIRING”, numRows = 3
输出:“PAHNAPLSIIGYIR”
示例 2:
输入:s = “PAYPALISHIRING”, numRows = 4
输出:“PINALSIGYAHRPI”
解释:
P I N
A L S I G
Y A H R
P I
示例 3:
输入:s = “A”, numRows = 1
输出:“A”
题目要求的是将字符每一个字母按一个反的N字形进行填充,然后换行输出字符,我们就可以直接构造一个这样的反N字形数组,反N字形数组里面的数字是字符串中字符的序号,其他位置则填0。注意在模拟的过程中返回将序号-1。
我们观察也可以发现我们需要构造多大的N字形数组是有规律的(一个类似的V可以容纳多少个字符),所以我们可以先计算至少需要多大的数组空间,减少内存占用。
class Solution {
public String convert(String s, int numRows) {
if(numRows==1)return s;
int k=s.length()/(2*(numRows-1)+1)+1;//判断要多少个N
int [][]Nmodel=new int[numRows][numRows*k];
int sindex=1;
int i=-1,j=0;
for (; j < numRows*k; j++) {
for (i++; i < numRows; i++) {
Nmodel[i][j]=sindex;sindex++;
if(sindex>s.length())break;
}
if(sindex>s.length())break;
i--;
for (i--; i >=0; i--) {
j++;
Nmodel[i][j]=sindex;sindex++;
if(sindex>s.length())break;
}
if(sindex>s.length())break;
j--;i++;
}
StringBuilder strb=new StringBuilder();
for (int l = 0; l < numRows; l++) {
for (int m = 0; m < numRows*k; m++) {
if(Nmodel[l][m]!=0)
strb.append(s.charAt(Nmodel[l][m]-1));
}
}
return new String(strb);
}
}
前面构造出一个模拟的数组还是操作有些复杂,我们可以发现N字形数组每一行都是有规律的变换的。
当numRows
=3,我们发现每一行左右相邻数字相隔3、1、3个数。
当numRows
=4,我们发现每一行左右相邻数字单数相隔5、3、1、5,双数相隔5、1、3、5个数。
当numRows
=5,我们发现每一行左右相邻数字单数相隔7、5、3、1、7,双数相隔7、1、3、5、7个数。
于是我们先创建行变化的numRows
*2 大小的数组,再直接构建这样的序号规律数组,其中利用一个递增的 tag %2
标志单双数。这样只需要按顺序写数组,按顺序读数组,前面一种方法中间的空白处填0的问题也没了。
class Solution {
public String convert(String s, int numRows) {
if(numRows==1)return s;
int [][]Nmodel=new int[numRows][s.length()/2+1];
int[][]change=new int[numRows][2];
change[0][0]=numRows*2-3;
change[numRows-1][0]=numRows*2-3;
for (int i = 1; i < numRows-1; i++) {
change[i][0]=change[i-1][0] - 2;
}
for (int i = 0; i < numRows; i++) {
change[i][1]=change[numRows-1-i][0];
}
Nmodel[0][0]=0;
for (int i = 1; i < numRows; i++) {
Nmodel[i][0]=Nmodel[i-1][0]+1;
}
int tag=1;
for (int i = 0; i < numRows; i++,tag=1) {
for (int j = 1; j < s.length()/2+1; j++) {
tag++;
Nmodel[i][j]=Nmodel[i][j-1]+change[i][tag%2]+1;
}
}
StringBuilder strb=new StringBuilder();
for (int l = 0; l < numRows; l++) {
for (int m = 0; m < s.length()/2+1; m++) {
if(Nmodel[l][m]>=s.length())break;
strb.append(s.charAt(Nmodel[l][m]));
}
}
return new String(strb);
}
}
前面一种方法写了数组又马上读数组,那我们也可以不构建序号数组,直接通过计算序号变化就进行字符串的添加,可以在上一个方法上面继续改。当然前面的行变化numRows
*2 大小的数组是需要提前写一下,然后判断字符序号是否超出字符串长度,就进行下一行。
class Solution {
public String convert(String s, int numRows) {
if(numRows==1)return s;
int[][]change=new int[numRows][2];
change[0][0]=numRows*2-3;
change[numRows-1][0]=numRows*2-3;
for (int i = 1; i < numRows-1; i++) {
change[i][0]=change[i-1][0] - 2;
}
for (int i = 0; i < numRows; i++) {
change[i][1]=change[numRows-1-i][0];
}
StringBuilder strb=new StringBuilder();
int tag=1;
int bfindex,tindex;
for (int i = 0; i < numRows; i++,tag=1) {
bfindex=i;
if(bfindex>=s.length())break;
strb.append(s.charAt(bfindex));
for (int j = 1; j < s.length()/2+1; j++) {
tag++;
tindex=bfindex+change[i][tag%2]+1;
if(tindex>=s.length())break;
strb.append(s.charAt(tindex));
bfindex=tindex;
}
}
return new String(strb);
}
}
返回第一页。☝
☕物有本末,事有终始,知所先后。
☝☝☝☝☝我的CSDN☝☝☝☝☝☝