今回は paiza の「シーザー配列」の問題に挑戦!
問題概要
- 配列
Aが与えられる- 長さ
N - 各要素は
0以上M未満の整数
- 長さ
- シーザー配列操作
- 配列全体に対して好きな回数「
1を足してMで割った余りに置き換える」を行える
- 配列全体に対して好きな回数「
-
Q個のクエリが与えられる- 各クエリには (
ind_i, S_i) がある - 「配列の
ind_i番目の要素がS_iになるようなシーザー配列を作れ」
- 各クエリには (
- 出力
- 各クエリに対して、シフト後の配列を空白区切りで出力
- 各クエリに対して、シフト後の配列を空白区切りで出力
入力例:
3 5
1 4 0
3
1 3
2 0
3 3
出力例:
3 1 2
2 0 1
4 2 3
✅OK例:
const rl = require('readline').createInterface({ input: process.stdin });
const lines = [];
rl.on('line', line => lines.push(line));
rl.on('close', () => {
const [N, M] = lines[0].split(' ').map(Number);
const A = lines[1].split(' ').map(Number);
const Q = Number(lines[2]);
const q = lines.slice(3).map(q => q.split(' ').map(Number));
for (let i = 0; i < Q; i++) {
let [idx, S] = q[i];
idx--;
if (S === A[idx]) {
console.log(A.join(' '));
continue;
}
const arr = [...A];
while (S !== arr[idx]) {
for (let j = 0; j < arr.length; j++) {
arr[j] += 1;
arr[j] %= M;
}
}
console.log(arr.join(' '));
}
});
while で 1 回ずつ増やすやり方だと M が大きくなると非効率
✨OK例:
const rl = require('readline').createInterface({ input: process.stdin });
const lines = [];
rl.on('line', line => lines.push(line));
rl.on('close', () => {
const [N, M] = lines[0].split(' ').map(Number);
const A = lines[1].split(' ').map(Number);
const Q = Number(lines[2]);
const queries = lines.slice(3).map(line => line.split(' ').map(Number));
for (let i = 0; i < Q; i++) {
let [idx, S] = queries[i];
idx--; // 0-indexedに直す
// Sになるまでの加算回数
const k = (S - A[idx] + M) % M;
// 配列を k だけ加算
const ans = A.map(x => (x + k) % M);
console.log(ans.join(' '));
}
});
🔍コードの流れ
- 標準入力の準備
-
readlineモジュールを使って、入力された行をlines配列に順番に格納。 - 入力の読み取り
- 1 行目から
NとMを取得(配列の長さとmoduloの値) - 2 行目から配列
Aを取得 - 3 行目からクエリの数
Qを取得 - 4 行目以降から各クエリ
[ind_i, S_i]を取得してqueries配列に格納
- 1 行目から
- クエリごとの処理
- クエリのインデックスを 0 始まりに変換(
idx--) - 指定された要素が
Sになるまでの加算回数kを計算
→(S - A[idx] + M) % Mで求める
※負の値が出ないように「Mを足してから% M」を取る
- クエリのインデックスを 0 始まりに変換(
- 配列全体に加算
- 配列
Aの各要素にkを加算してmod Mを取る - 新しい配列
ansを作成
- 配列
- 結果出力
- 配列
ansを空白区切りで出力
- 配列
📝まとめ
- シーザー暗号的な配列操作
- 配列全体を「1 ずつ増やす
% M」の操作で変換できる - 指定された要素を
Sにするために必要な操作回数は 差を% Mで計算して求める
- 配列全体を「1 ずつ増やす
- 操作回数
kの計算k = (S - A[idx] + M) % M-
+ Mは負の値を避けるため - % M
で範囲を0~M-1` に収める
- 配列全体に一括加算で効率化
-
whileで 1 回ずつ増やすより、計算したkを一括で加算する方が速い - 配列
A.map(x => (x + k) % M)で簡単に実現可能
-
- インデックス調整
- 入力は 1 始まりなので、プログラム内では 0 始まりに直す(
idx--)
- 入力は 1 始まりなので、プログラム内では 0 始まりに直す(