diff --git a/kkanazawa/src/SpiralDecoder.java b/kkanazawa/src/SpiralDecoder.java new file mode 100644 index 0000000000000000000000000000000000000000..45c2046f7f10e862ac480c4b012f43755cfa9e30 --- /dev/null +++ b/kkanazawa/src/SpiralDecoder.java @@ -0,0 +1,216 @@ +package paiza.src; + +import java.lang.reflect.Array; +import java.util.Arrays; +import java.util.Scanner; + +/** + * 受け取った二次元の文字リストをうずまき型で解読する + * + * @author 金澤継心 + */ +public class SpiralDecoder { + + public static void main(final String[] args) { + Scanner scanner = new Scanner(System.in); + final int rowCount = scanner.nextInt(); // 入力値の行の数 + final int columnCount = scanner.nextInt(); // 入力値の列の数; + final int startIndex = scanner.nextInt(); // 読み始まる文字のインデックス + final int endIndex = scanner.nextInt(); // 読み終える文字のインデックス + final String barrirchar = "@"; + + // もととなる二次元の文字リストのを入力から受け取る + final String[][] cipher = inputCipher(scanner, rowCount, columnCount); + scanner.close(); + + // 入力値の二次元の文字リストの周りをNULLで埋める + final String[][] paddedCipher = paddingCipher(cipher, barrirchar); + + // 解読処理 + final String selectedDecode = decodingCipher(rowCount, columnCount, startIndex, endIndex, + paddedCipher, barrirchar); + System.out.println(selectedDecode); + } + + /** + * もととなる文字列を入力から受け取る + * + * @param scanner 入力を受け取るスキャナー + * @param rowCount 文字リストの行数 + * @param columnCount 文字リストの列数 + */ + public static String[][] inputCipher(final Scanner scanner, final int rowCount, + final int columnCount) { + String[][] cipher = new String[rowCount][columnCount]; + for (int i = 0; i < rowCount; i++) { + String inputRow = scanner.next(); + cipher[i] = inputRow.split(""); + } + return cipher; + } + + /** + * 文字リストの周りを@で埋める + * + * @param originCipher もととなる文字リスト + */ + public static String[][] paddingCipher(final String[][] originCipher, final String barrirchar) { + final int ROW_COUNT = Array.getLength(originCipher); + final int COLUMN_COUNT = Array.getLength(originCipher[0]); + String[][] paddedCipher = new String[ROW_COUNT + 2][COLUMN_COUNT + 2]; + + for (String[] o : paddedCipher) { + Arrays.fill(o, barrirchar); + } + + // 元の文字リストを"@"しかない文字リストの中央にコピーする + for (int i = 0; i < ROW_COUNT; i++) { + System.arraycopy(originCipher[i], 0, paddedCipher[i + 1], 1, COLUMN_COUNT); + } + return paddedCipher; + } + + /** + * 周りをNULLで埋めた文字リストの解読 @にぶつかったら方向転換するイメージでうずまき状に要素を移動する + * 移動した順に要素を復号用の配列に移していき、元の配列の要素はNULLに置き換える + * + * @param rowCount 文字列リストの行数 + * @param columnCount 文字列リストの列数 + * @param startIndex 解読後読み始める文字の順番 + * @param endIndex 解読後読み終える文字の順番 + */ + public static String decodingCipher(final int rowCount, final int columnCount, + final int startIndex, + final int endIndex, final String[][] cipher, final String barrirchar) { + // 現在いる要素を示すカーソルとして使うクラス + DecodeCursor cursor = new DecodeCursor(); + // NULLでかこっているため、始まりのインデックスは行、列ともに1から + cursor.setRowIndex(1); + cursor.setColumnIndex(1); + // 移動する方向を表すインスタンス変数 + cursor.setDirection(0); + // 現在移し終えた文字数のカウンタ + int charCounter = 0; + // この配列に文字移して最終的には復号した順番に文字が並ぶ + String[] decodedResult = new String[rowCount * columnCount]; + + // 初期位置から初めて@にぶつかるまでの解読処理 + while (cipher[cursor.getRowIndex()][cursor.getColumnIndex()] != barrirchar) { + decodedResult[charCounter] = cipher[cursor.getRowIndex()][cursor.getColumnIndex()]; + cipher[cursor.getRowIndex()][cursor.getColumnIndex()] = barrirchar; + charCounter++; + cursor.shifting(); + } + cursor.changeDirection(); + + // 初めて@にぶつかってから解読し終わるまでの処理 + while (charCounter < rowCount * columnCount) { + // @の要素に移動した際、カーソルの位置を戻す処理 + cursor.modifying(); + // @のぶつかるまで一方向に要素を読み取り続ける + while (cipher[cursor.getRowIndex()][cursor.getColumnIndex()] != barrirchar) { + decodedResult[charCounter] = cipher[cursor.getRowIndex()][cursor.getColumnIndex()]; + cipher[cursor.getRowIndex()][cursor.getColumnIndex()] = barrirchar; + charCounter++; + // カーソルを現在進むべき方向に移動させる + cursor.shifting(); + } + // 方向転換の処理 + cursor.changeDirection(); + } + return String.join("", decodedResult).substring(startIndex - 1, endIndex); + } + +} + + +/** + * 現在読み取っている要素を示すカーソルとして使うクラス + * + */ +class DecodeCursor { + private int direction;// 方向を示す 4つの方向はこの値を4で割った剰余として区別している + private int rowIndex;// 現在の行の位置 + private int columnIndex;// 現在の列の位置 + final static int RIGHT = 0; + final static int DOWN = 1; + final static int LEFT = 2; + final static int UP = 3; + + public int getDirection() { + return direction; + } + + public void setDirection(final int DIRECTION) { + this.direction = DIRECTION; + } + + public void changeDirection() { + this.direction++; + } + + public int getRowIndex() { + return rowIndex; + } + + public void setRowIndex(final int ROW_INDEX) { + this.rowIndex = ROW_INDEX; + } + + public int getColumnIndex() { + return columnIndex; + } + + public void setColumnIndex(final int COLUMN_INDEX) { + this.columnIndex = COLUMN_INDEX; + } + + // NULL(要検討)の要素に移動した際、カーソルの位置を戻す処理 + // 4つの方向はdirectionを4で割った剰余として区別する + // 0:右 1:下 2:左 3:上 + public void modifying() { + final int directionNum = this.direction % 4; + + switch (directionNum) { + case RIGHT: + this.columnIndex++; + this.rowIndex++; + break; + case DOWN: + this.columnIndex--; + this.rowIndex++; + break; + case LEFT: + this.columnIndex--; + this.rowIndex--; + break; + case UP: + this.columnIndex++; + this.rowIndex--; + break; + } + } + + // カーソルを現在進むべき方向に移動させる + // 4つの方向はdirectionを4で割った剰余として区別している + // 0:右 1:下 2:左 3:上 + public void shifting() { + final int directionNum= this.direction % 4; + + + switch (directionNum) { + case RIGHT: + this.columnIndex++; + break; + case DOWN: + this.rowIndex++; + break; + case LEFT: + this.columnIndex--; + break; + case UP: + this.rowIndex--; + break; + } + } +}