《算法设计与分析》第二周作业
标签(空格分隔): 课堂作业
姓名:李**
学号:16340114
题目:ZigZag Conversion(https://leetcode.com/problems/zigzag-conversion/description/)
题目概要
输入一个字符串,将该字符串按类z形状排列,再按横向顺序输出新的字符串,详情点击题目链接。
思路
尝试一
创建一个矩阵,把输入字符串按照题目规则填入矩阵中,矩阵剩余位置留空,再按行遍历矩阵,得到目的输出。这样做貌似很暴力,而且当行数增多的时候,矩阵里的空位会变得很多,且遍历的开销也会变得非常大。这种解法似乎不现实。
尝试二
采用分而治之的思想,将按z型排列的字符串分成若干个相同的组(如图所示),在一个组内建立输入字符串与输出字符串的映射函数,继而推广到所有组别,得出通用的转换公式。
注意到,在分组的时候,最后一组可能与其他组不同,这使得映射函数的建立变得困难,因此,先假定最后一组与其他组的模式都是相同的,之后再处理最后一组(参差不齐)的问题。
在建立转换公式之前,先计算一些重要的参数:
每组的字符个数(靠观察可得):entitiesInSet = 2 * numRows - 2;
总组数(同样靠观察可得):numSet = ⌈ inputString.length() / entitiesInSet ⌉(向上取整);
在每一组中,分为四种情况:
1.组内第一个字符
稍微观察一下(感觉这题就是在找规律),每组的第一个字符对应输出字符下标为 分组序号setIndex,即
outputString[setIndex] = inputString[oldStringIndex];
2.组内第一列中间字符(除去第一个和最后一个字符)
第二行的元素对应的下标为 【第一行的字符数 + 组号 * 2】
第三行的元素对应的下标为 【第一行的字符数 + 第二行的字符数 + 组号 * 2】
···
第i行的元素对应的下标为 【第一行的字符数 + 第二行的字符数 + ··· + 第i - 1行的字符数 + 组号 * 2】
第一行的字符数为总组数numSet,剩余行的字符数为总组数乘二numSet * 2。
可以得到对第一列中第i个(对应第i+1行)字符。(图中第一组的A是第1个(i = 1),Y是第二个 (i = 2))对应的下标为 【组数 * (2 * i - 1) + 组号 * 2】, 即
outputString[numSet * (2 * i - 1) + setIndex * 2] = inputString[oldStringIndex];
3.组内第一列最后一个字符
用类似情况2的推算,对应的下标为 【除最后一行的总字符数(第一行的字符数 + 第2行 ~ 第numRows - 1行)的字符数 + 组号】,即
outputString[numSet * (2 * numRows - 3) + setIndex] = inputString[oldStringIndex];
4.组内在斜线上的元素
与第2中情况类似,只是对应的下标多了1,组数 * (2 * i - 1) + 组号 * 2 + 1,即
outputString[numSet * (2 * i - 1) + setIndex * 2 + 1] = inputString[oldStringIndex];
这四种情况都处理完之后,就开始处理最后一组(参差不齐)的问题。
其实这个问题还蛮好处理的,将最后一组单独拿出来处理,先假定最后一组是完整的,与其他组一样处理,在遇到输入字符不存在的位置时,将一个特殊符号(如“*”,输入字符串里没有该符号)放入输出字符串中。将全部完成后再次遍历输出字符,遇到该特殊字符(*)时将该字符剔除,即可得到最终答案。
具体实现
先计算“思路”中的两个重要参数。
再以组号为参数进行循环,处理除最后一组之外的组。同时使用一个下标变量,按顺序对输入字符进行处理,每处理完一个字符该变量自增。
在每组中按照思路中的4种情况按顺序处理,将得出的公式转化为代码即可,特别地,在第3种情况中,随着输入字符下标的递增,行数是递减的,这时只要将循环变量递减即可。
之后处理最后一组,使用宏定义检测下标是否越界,如越界,则将‘*’输出到输出字符串。
最后创建一个新的字符串,将中间输出字符串的非’*’字符吸收即可。
心得
虽然这次没用到课堂上讲的大师定理,但是分治思想的应用还是使得这个问题简单了许多(比用矩阵蛮干好多了不是吗),将一个大问题分解成一个个的小问题,这样思考起来不会那么吃力,而且把小问题解决了大问题也就自然而然地就解决了。
源码:
#define entityOrBubble (oldStringIndex >= s.length() ? '*' : s[oldStringIndex]);
class Solution
{
public:
string convert(string s, int numRows)
{
if (s.length() == 0 || numRows == 1)
return s;
int entitiesInSet = 2 * numRows - 2;
int numSet = ceil( (float)s.length() / entitiesInSet );
string tempString(numSet * entitiesInSet, '*');
// common set
for (int setIndex = 0; setIndex <= numSet - 2; ++setIndex)
{
int oldStringIndex = setIndex * entitiesInSet;
//first entity in each set
tempString[setIndex] = s[oldStringIndex];
oldStringIndex++;
//the remain entities of the first column in each set except the last one
for (int i = 1; i <= numRows - 2; ++i)
{
tempString[numSet * (2 * i - 1) + setIndex * 2] = s[oldStringIndex];
oldStringIndex++;
}
//the last entity of the first column in each set
tempString[numSet * (2 * numRows - 3) + setIndex] = s[oldStringIndex];
oldStringIndex++;
//the remain entities of each set
for (int i = numRows - 2; i >= 1; --i)
{
tempString[numSet * (2 * i - 1) + setIndex * 2 + 1] = s[oldStringIndex];
oldStringIndex++;
}
}
// the last set
{
int setIndex = numSet - 1;
int oldStringIndex = setIndex * entitiesInSet;
//first entity in each set
tempString[setIndex] = s[oldStringIndex];
oldStringIndex++;
//the remain entities of the first column in each set except the last one
for (int i = 1; i <= numRows - 2; ++i)
{
tempString[numSet * (2 * i - 1) + setIndex * 2] = entityOrBubble;
oldStringIndex++;
}
//the last entity of the first column in each set
tempString[numSet * (2 * numRows - 3) + setIndex] = entityOrBubble;
oldStringIndex++;
//the remain entities of each set
for (int i = numRows - 2; i >= 1; --i)
{
tempString[numSet * (2 * i - 1) + setIndex * 2 + 1] = entityOrBubble;
oldStringIndex++;
}
}
string finalString(tempString.length() + 1, '\0');
int finalStringIndex = 0;
for (int i = 0; i < tempString.length(); ++i)
{
if(tempString[i] != '*')
{
finalString[finalStringIndex] = tempString[i];
finalStringIndex++;
}
}
return finalString;
}
};