LeetCode(6.Z字形变换[中等])

题目

将一个给定字符串根据给定的行数,以从上往下、从左到右进行 Z 字形排列。

比如输入字符串为 “LEETCODEISHIRING” 行数为 3 时,排列如下:

在这里插入图片描述

之后,你的输出需要从左往右逐行读取,产生出一个新的字符串,比如:“LCIRETOESIIGEDHN”。

请你实现这个将字符串进行指定行数变换的函数:

string convert(string s, int numRows);
示例 1:

输入: s = “LEETCODEISHIRING”, numRows = 3
输出: “LCIRETOESIIGEDHN”
示例 2:

输入: s = “LEETCODEISHIRING”, numRows = 4
输出: “LDREOEIIECIHNTSG”
解释:

LeetCode(6.Z字形变换[中等])_第1张图片

题目解析及优化

  1. 我自己愿意称下面的方法为暴力法

    既然想要将字符串转变成为Z形输出,那就用一个二维数组,将字符串依次填到对应位置上,然后按行进行输出即可。直接上代码:

    class Solution {
    public:
        string convert(string s, int numRows) {
            char arr[numRows][500];
            //初始化二维数组
            for (int i = 0; i < numRows; i++) {
                for (int j = 0; j < 500; j++) {
                    arr[i][j] = '#';
                }
            }
            //设置每个循环的Z多少个字符(T),横向占多少行(line)
            int T = numRows == 1 ? 1 : 2 * numRows - 2;
            int line = numRows == 1 ? 1 : 1 + numRows - 2;
            for (int i = 0; i < s.size(); i++) {
                int n = i / T;
                int pos = i % T + 1;
                if (pos > numRows) {
                    arr[2 * numRows - pos - 1][n * line + pos - numRows] = s[i];
                } else {
                    arr[pos - 1][n * line] = s[i];
                }
            }
            string ans;
    		//逐行扫描添加到答案中
            for (int i = 0; i < numRows; i++) {
                for (int j = 0; j < 500; j++) {
                    if (arr[i][j] != '#') {
                        ans.push_back(arr[i][j]);
                    }
                }
            }
            return ans;
        }
    };
    

    对这种解法还能做进一步的优化。‘#’字符只起到了方便观看的作用,而没有实际用途。对字符串s逐步遍历的时候,直接找出应该跟在哪一行后即可。优化之后代码如下:

    class Solution {
    public:
        string convert(string s, int numRows) {
            if (numRows == 1)return s;
            string ans;
            vector  result(numRows);
            bool flag = true;
            int line = 0;
            for (int i = 0; i < s.size(); i++) {
                result[line].push_back(s[i]);
                if ((line == 0 || line == numRows - 1) && i > 0) {
                    flag = !flag;
                }
                line += flag ? 1 : -1;
            }
            for (int i = 0; i < numRows; i++) {
                ans = ans + result[i];
            }
            return ans;
        }
    };
    
  2. 直接输出

    其实在写第一种方法途中找每个字符在二维数组中的位置,就基本可以想到第二种方法,对初始字符串最直接处理并输出。第一种方法是从初始字符串找每个字符在新字符串中的位置,第二种是从目标字符串思考,每个字符串来自原来字符串的上面地方。

    第0行的字符位于索引的k(2*numRows-2)处。

    第numRows-1行的字符位于(2*numRows - 2) + numRow-1处

    中间的行位于 (2 * numRows-2) + i 和(2 * numRows -2) -i处

    以上的坐标为第一个周期内,其余的乘倍数即可

    代码如下:

    class Solution {
    public:
        string convert(string s, int numRows) {
            if (numRows == 1) {
                return s;
            }
            string ans;
            int n = s.size();
            int T = 2 * numRows - 2;
            for (int i = 0; i < numRows; i++) {
                for (int j = 0; i + j < n; j += T) {
                    ans.push_back(s[i + j]);
                    //在中间行时额外输一次
                    if (i > 0 && i < (numRows - 1) && j + T - i < n) {
                        ans.push_back(s[j + T - i]);
                    }
                }
            }
            return ans;
        }
    };
    

    心得及总结

    • 看到题目最先想到的应该是二维数组去遍历,但是用二维数组会浪费一些空间,此时可以根据字符串遍历的特征去改变策略。拿到题目有基本的做法时多想一步,思考这种做法的本质去优化;
    • 在每一次循环的时候输出次数不一样时,将比较另类的判断并输出;
    • 在第一种方法的优化过程中,判断转换方向的时候忘记了行数等于0的时候,解决方法除了增加转换条件还可以直接将方向改为向上也可以;

你可能感兴趣的:(字符串,算法,leetcode,c++)