😇

LeetCode 6. Zigzag Conversion を完全に理解する by TypeScript

2022/07/31に公開

Zigzag Conversion

問題文

The string "PAYPALISHIRING" is written in a zigzag pattern on a given number of rows like this: (you may want to display this pattern in a fixed font for better legibility)

P   A   H   N
A P L S I I G
Y   I   R
And then read line by line: "PAHNAPLSIIGYIR"

Write the code that will take a string and make this conversion given a number of rows:

string convert(string s, int numRows);

日本語 by DeepL

PAYPALISHIRING "という文字列は、次のように与えられた行数にジグザグに書かれています(読みやすくするために、このパターンを固定フォントで表示するとよいでしょう)。

P A H N
A P L S I G
Y I R
そして、一行ずつ読みます。"pahnaplsiigyir"

文字列を受け取り、行数を指定してこの変換を行うコードを書きます。

string convert(string s, int numRows);

例題

Input: s = "PAYPALISHIRING", numRows = 4
Output: "PINALSIGYAHRPI"
Explanation:
P     I    N
A   L S  I G
Y A   H R
P     I

検討

つまり、paypalという文字があって高さ3だったら
最初に縦に降りていって、次に斜めに上に戻っていく。
文字を配列にして、objectにrow0,row1,row2みたいな配列を持ち、そこに1個ずつ入れていくのかなぁ……と考えた。そしてrow0,1,2のカウントの部分を変えていくのかな……みたいな。

回答

function convert(s: string, numRows: number): string {
  /* 1列のときは、そのまま文字を返す */
  if (numRows === 1) return s;

  /*
    stackを定義する。
    縦に3列あるなら1列目、2列目、3列目とつくる
    fillというのは埋めておくやつ。
    ['','','']->ここに文字をためていく
  */
  const stack = new Array(numRows).fill("");

  /* indexカウンタを定義 */
  let idx = 0;

  /* そのまま下に移動するか、ジグザグかのboolean */
  let isZigZag = false;

  /* 文字の数だけループする */
  for (let char of s) {
    /*
      最初はindexが0であるから
      stack[0]に今回の文字を入れる。というか足していく。
      string+string
      このstack[0]が一番上の文字たち。
      stack[1]が2列めの文字たち。という扱い。
      0,1,2と下に降りてきて、次には1,0と上に上がっていく。
    */
    stack[idx] += char;

    /*
      indexがnumRows -1と等しいとき……。
      つまりnumRowsが3のときは
      0,1,2で3回目のループが2で、numRows -1も2なのだ。
      たてに3個降りてきて
      しかも!isZigZagということは
      次から折り返しなのでisZigZagのフラグをtrueに変更する
    */
    if (idx === numRows - 1 && !isZigZag) isZigZag = true;

    /* 
      indexが0、つまり一番上に戻ってきて、いまzigzagなのであれば
      zigzagから普通の下に降りていくモードに戻す。
      ということでisZigZagをfalseにする
    */
    if (idx === 0 && isZigZag) isZigZag = false;

    /*
      isZigZagなら……つまり下から上に戻っているなら
      indexは--で減らしていく(上に戻っていく)
      zigzagじゃないなら降りていくのでindex++で増やしていく
    */
    isZigZag ? idx-- : idx++;
  }

  /* 最後にスタックした配列をジョインで文字にして回答 */
  return stack.join("");
}

考え方はだいたいあってた。

以下、このメモを見てプログラムするときのメモ書き。
最終的には見ずに解けるようになると吉。

  1. 1列のときの条件に対応する
  2. 列ごとの配列(stack)を作成する
  3. いまどこの列に入れているのかのindexカウンタをつくる
  4. いま縦に降りているかzigzagなのかのbooleanをつくる
  5. 文字の数だけループする
  6. 現在のstackのrowに今回の文字を追加する
  7. 列の一番下に来たときにzigzagをtrueにする
  8. 列の一番上に来たときにzigzagをfalseにする
  9. いまzigzagならindexのカウンタを減らし、zigzagでなければindexのカウンタを増やす
  10. 最後に配列を文字にする

zigzagというのがどういう状態なのかイメージをつかめば大丈夫かな。

自分なりに解き直してみた。
indexでisZigZagかどうかは決まるので、いまがisZigZagかどうかはあんまり関係ない気がした。
実際に通ったので、条件的には要らないのかな?

function convert(s: string, numRows: number): string {
    if(numRows === 1) return s;
    
    let stack = [...Array(numRows)].map(i => '');
    let index = 0;
    let isZigZag = false;
    
    for (const str of s) {
        stack[index] = stack[index] + str;
        if(index === numRows -1) isZigZag = true;
        if(index === 0) isZigZag = false;
        isZigZag ? index-- : index++;
    }
    return stack.join('');
};

Discussion