题目:Z字形变换
第一次看到这道题,恍惚之间以为回到了儿童时期在做数学卷子。这tm就是个小学奥数题吧。
然后面对这个小学奥数题,我花费了一个下午(顺路刷完了毒奶粉账号的疲劳);
虽然花费的时间比较多但是提交之后发现又是一个超过百分之百的,感觉心情还不错。
代码如下:
static const auto io_speed_up = []() { std::ios::sync_with_stdio(false); cin.tie(nullptr); return 0; }(); class Solution { public: string convert(string s, int numRows) { if (numRows == 1) return s; int length = s.size(); int groupNum = numRows + numRows - 2; bool isShort = false; if (groupNum > length) { s = s + string(groupNum - length, '0'); length = groupNum; isShort = true; } int quotient = length / groupNum; int remainder = length % groupNum; vector<int> num(groupNum); vector<int> sum(numRows); int i = 0; int j = 0; for (i = 0; i < remainder; ++i) num[i] = 1; sum[1] = quotient + num[0]; for (int i = 2; i < numRows; ++i) { j = i - 1; sum[i] = sum[j] + quotient + quotient + num[j] + num[groupNum - j]; } i = 0; j = 0; int group = 0; int doubleRow = numRows - 1; string res(s); while (group < quotient) { res[sum[0]++] = s[i++]; j = 1; while(j < doubleRow) res[sum[j++]++] = s[i++]; res[sum[doubleRow]++] = s[i++]; while(j > 1) res[sum[--j]++] = s[i++]; ++group; } j = 0; int k = 0; while(j < doubleRow && k++ < remainder) res[sum[j++]++] = s[i++]; if (i < length) { res[sum[doubleRow]++] = s[i++]; ++k; } while(j > 1 && k++ < remainder) res[sum[--j]++] = s[i++]; if (isShort) { char* temp = new char[length]; for (i = 0, j = 0; i < length; ++i) { if(res[i] != '0') temp[j++] = res[i]; } temp[j] = '\0'; res = string(temp); } return res; } };
在这道题中,我把Z字型的一部分分为一组,即竖+斜等于一组,然后确定了两段每组会出现一个字符,然后中间的部分每组会出现两个字符。这里我们把输入为1的情况特殊处理,然后输入为2的情况,在代码中会跳过。接着因为这个方法处理字符最低为一组,所以我们碰到不足一组的在后面补‘0’补齐,然后把求出来的结果中‘0’字符去除。对于每一组,我们又可以分出两种情况,1是从上到下即N的竖线,2是从下到上即N的斜线,例如对于题目中所给的"PAYPALISHIRING",3这种情况我们可以分为
P | A | H | N | |||
A | P | L | S | I | I | G |
Y | I | R |
我把字符分为顶端,从上到下,底端,从下到上四种情况来处理,然后在处理完所有的组之后,我把不足一组的特殊处理。
字符的处理我是开辟了一个行数大小的数组,然后记录每个数组的起始地方的字符下标,然后每处理好一个字符把该行要处理的字符下标+1;
测完我的方法之后我去看了看之前排第一的方法,代码如下:
static auto static_lambda = [] () { std::ios::sync_with_stdio(false); std::cin.tie(0); return 0; }(); class Solution { public: string convert(string s, int numRows) { int length = s.length(); if(length < numRows || numRows <= 1) return s; vector<string> r(numRows); int step = 1; int row = 0; for(int i = 0; i < length; i++) { if( row == numRows-1) step =-1; if(row == 0) step =1; r[row] += s[i]; row += step; } string result; for (int i=0; i<numRows; i++) result += r[i]; return result; } };
他的代码很巧妙,把每一行设置成一个字符串,然后也是分组,每组两种情况。他设置碰到顶端,让下次累加字符串所在的数组是现在数组+1,如果碰到底端则是现在数组-1,到最后再把所有的字符串加起来。这种方法不需要考虑最后可能余下一部分不到一组的情况。
我觉得这个代码每次字符串拼接浪费了时间,如果把他拼接的部分改为:计算好字符串大小然后分配数组,在数组范围内操作,最后分配一个答案大小的字符串再循环赋值,这样的话速度会比我的代码还要快,并且更容易理解。