题目描述:
Z 字形变换
将一个给定字符串 s 根据给定的行数 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); (提示: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”
解法一:
如图一:得出规律,差值2numrows-2,首尾行的起始下标只有一个,分别是0,numrows-1,中间行数起始下标有2个,下标相加是2numrows-2。(此规律容易理解些)
Java代码实现如下:
public String convert(String s, int numRows) {
/**找规律,以N型为规律,每个N型对应位置之间相差 2 * numRows - 2 下标
*/
//判断数组长度与numRows行数是否可成多个Z循环(>=1循环)
if (numRows >= 2 && s.length() > numRows) {
Map<Integer, ArrayList<Integer>> indexMaps = new HashMap<Integer, ArrayList<Integer>>();
Map<Integer, String> strMaps = new HashMap<Integer, String>();
//同一行的字符与起始位 相差 2 * numRows - 2 个位 定义差值
int diffValue = 2 * numRows - 2;
//初始化起始行的下标
ArrayList<Integer> firstList = new ArrayList<Integer>();
firstList.add(0);
indexMaps.put(0, firstList);
strMaps.put(0, s.substring(0, 1));
//获取后续遍历s的开始位置
int lastIndex = 0;
//中间行数有2个index,获取中间行数的起始index 下标相加是2*numrows-2
for (int i = 1; i < numRows - 1; i++) {
ArrayList<Integer> indexList = new ArrayList<Integer>();
indexList.add(i);
strMaps.put(i, s.substring(i, i + 1));
if (lastIndex < i) {
lastIndex = i;
}
//此处加判断,防止组不成一个Z的情况
if (s.length() > diffValue - i) {
indexList.add(diffValue - i);
strMaps.put(i, strMaps.get(i) + s.substring(diffValue - i, diffValue - i + 1));
if (lastIndex < diffValue - i) {
lastIndex = diffValue - i;
}
}
indexMaps.put(i, indexList);
}
//初始化最后一行的下标
ArrayList<Integer> endList = new ArrayList<Integer>();
endList.add(numRows - 1);
indexMaps.put(numRows - 1, endList);
strMaps.put(numRows - 1, s.substring(numRows - 1, numRows));
if(lastIndex<numRows - 1){
lastIndex=numRows - 1;
}
//若 s.length() > lastIndex + 1 开始遍历后续字符串
for (int i = lastIndex + 1; i < s.length(); i++) {
out:for (Map.Entry<Integer, ArrayList<Integer>> entry : indexMaps.entrySet()) {
ArrayList<Integer> list = entry.getValue();
for (int index : list) {
//规律 同一行的字符位置数 相差 2 * numRows - 2个位,起始2行只有一个index分别是0,N-1,中间行数有2个index,
// 即与起始位置相差x倍数个2* numRows - 2
//因此判断相除无余数即可
if ((i - index) % (diffValue) == 0) {
//每一行拼接字符
strMaps.put(entry.getKey(), strMaps.get(entry.getKey()) + s.substring(i, i + 1));
break out;
}
}
}
}
//将拼接好的行数,拼接起来输出
String res = "";
for (Map.Entry<Integer, String> entry : strMaps.entrySet()) {
res = res + entry.getValue();
}
return res;
} else {
//不满足,直接返回s
return s;
}
}
解法二:
在图一规律上继续找规律,发现
代码实现如下:
public String convert2(String s, int numRows) {
/**找规律,以N型为规律,每个N型对应位置之间相差 2 * numRows - 2 下标
*/
//判断数组长度与numRows行数是否可成多个Z循环(>=1循环)
if (numRows >= 2 && s.length() > numRows) {
//用数组存放结果
String[] resChars = new String[numRows];
//初始化数组
Arrays.fill(resChars, "");
//s拆分成数组
char[] chars = s.toCharArray();
//同一行的字符与起始位 相差 2 * numRows - 2,定义差值diffValue
int diffValue = 2 * numRows - 2;
for (int i = 0; i < chars.length; i++) {
//取余数
int mod = i % diffValue;
//小于numRows 正向取余排序
if (mod < numRows) {
resChars[mod] += chars[i];
} else {
//反向取余排序
resChars[diffValue - mod] += chars[i];
}
}
//将拼接好的行数,拼接起来输出
StringBuilder res = new StringBuilder();
for (String a : resChars) {
res.append(a);
}
return res.toString();
} else {
//不满足,直接返回s
return s;
}
}