令和7年度の科目B、その4問目です。訊かれているのは、この関数が何を作るかではなく、ある1行の条件式が「真となる」回数です。プログラムの意味が分かっても、数え方でつまずく問題です。
出題
まず原文のまま読んでみてください。記号の読み方は次の節にまとめてありますので、読めなくてもここでは問題ありません。
出典:令和7年度 基本情報技術者試験 科目B 公開問題 問4
次の記述中の に入れる正しい答えを,解答群の中から選べ。ここで,配列の要素番号は1 から始まる。
関数search は,二つの文字型の配列を,それぞれ引数data 及びkey で受け取り,data から,key の要素の並びと同じ並びを全て探し,その先頭の要素番号を全て格納した配列を返す。見つからなければ,要素数0 の配列を返す。
関数search をsearch({“a”, “b”, “a”, “b”, “c”, “a”, “b”, “c”}, {“a”, “b”, “c”}) として呼び出すと,/*** β ***/の行の条件式が真となる回数は 回である。
〔プログラム〕
/* keyは,要素数1以上の配列である */ ○整数型の配列: search(文字型の配列: data, 文字型の配列: key) 整数型: i, j, lenData, lenKey 整数型の配列: result ← {} // 要素数0の配列 lenData ← dataの要素数 lenKey ← keyの要素数 /* (lenData - lenKey + 1) が0以下のときは繰返し処理を実行しない */ for (i を 1 から (lenData - lenKey + 1) まで 1 ずつ増やす) for (j を 1 から lenKey まで 1 ずつ増やす) // α if (data[i + j - 1] が key[j] と等しい) /*** β ***/ if (j が lenKey と等しい) resultの末尾 に iの値 を追加する endif else αの行から始まる繰返し処理を終了する endif endfor endfor return result
解答群
ア 1イ 2ウ 3エ 4オ 5
カ 6キ 7ク 8ケ 9コ 10
答えだけ先に見る
正解は ク(8回)。ただし、この記事は解答群を見ずに数える順で進みます。解答群は 1 から 10 の数字が並んでいるだけなので、見比べても何も決まりません。
この問題に出てくる記号と言葉
読めない記号があったときだけ開いてください。先に全部読む必要はありません。
擬似言語の記号と言葉 10コ の読み方をひらく
| 書き方 | 読み方 |
|---|---|
← |
左に書き込む。lenData ← dataの要素数 なら、右で求めた値を lenData に入れます |
○整数型の配列: search(…) |
行頭の ○ はここから1つのまとまった処理が始まるという印。整数型の配列 は、そのまとまりが最後に作り出すものの種類です |
整数型: i, j, lenData, lenKey |
使う箱の名前をまとめて宣言しているだけの行。ここでは何も起こりません |
/* … */// … |
どちらもコメント(人が読むための覚え書き)で、実行されません。この問題では、その覚え書きに付けた α と β という行の名前が本文から参照されています |
search(文字型の配列: data, 文字型の配列: key) |
かっこの中は呼ぶときに渡された配列が入る変数です。search({"a", …}, {"a", "b", "c"}) と呼べば、data が1つ目、key が2つ目になります |
dataの要素数 |
配列 data の枡の数。この呼び出しでは 8 です |
data[3] |
配列 data の 3番目の枡。角かっこの中は要素番号で、1 から数えます。data[i + j - 1] のように式も書けます |
return |
そこで関数を終えて、値を返すという意味です。return result なら、配列 result を返して終わります |
{} |
要素数0の配列。枡が1つも無い状態です |
resultの末尾 に iの値 を追加する |
配列 result のうしろに枡を1つ増やして、そこに i の値を入れます |
αの行から始まる繰返し処理を終了する |
// α と書かれた 内側の for だけを、その場でやめます。外側の for は続きます |
コードを読む ── どこまで、何と何を、いつやめる
この問題は空欄がプログラムの外にあります。だから「何を数えるのか」は、コードを読まないと決まりません。注釈を付けて、原文をもう一度置きます。
○整数型の配列: search(文字型の配列: data, 文字型の配列: key)
見つけた先頭の要素番号を並べた配列を返す
整数型: i, j, lenData, lenKey
使う箱の宣言。ここでは何も起こらない
整数型の配列: result ← {}
見つけた番号を入れる。はじめは空
lenData ← dataの要素数
この呼び出しでは 8
lenKey ← keyの要素数
この呼び出しでは 3
for (i を 1 から (lenData - lenKey + 1) まで 1 ずつ増やす)
i を 1 から 8 - 3 + 1 = 6 まで動かす
for (j を 1 から lenKey まで 1 ずつ増やす) // α
j を 1 から lenKey(=3)まで。この間 i は動かない
if (data[i + j - 1] が key[j] と等しい) /*** β ***/
ここが数える行。data[i + j - 1] と key[j] が等しければ真
if (j が lenKey と等しい)
β が真のときだけ来る。真になるのは最後の j まで届いたときだけ
resultの末尾 に iの値 を追加する
先頭の要素番号を記録する
endif
else
αの行から始まる繰返し処理を終了する
α は内側の for の名前。内側だけをここで終わらせる
endif
endfor
endfor
return result
見つけた番号を並べて返す(中身はこれから数えます)
※ 右側の注釈は当サイトで書き加えたものです。左のプログラムは原文のままですが、説明に使わないコメント2行は省いています(全文は上の〔プログラム〕にあります)。
数えるのに要るのは、ここから読み取る3つだけです。
① data[i + j - 1] と key[j] ── i を止めて j を動かす
内側の for が動かすのは j だけで、1 から lenKey(=3)まで。そのあいだ i は変わりません。まず i = 1 で、添字の式に数字を入れて、配列の上で1つずつ見ていきます。
添字の式 data[i + j - 1] を、配列の上でたどる
上=配列 data(枡の番号は要素番号)/下=配列 key を i 番に重ねたもの(枡の番号は j)。琥珀=その j で組になる枡。「次へ」で1つずつ進みます。
j = 1 → data[1 + 1 - 1] = data[1] と key[1]
data
1a
2b
3a
4b
5c
6a
7b
8c
key
1a
2b
3c
data の 1番(a)と、key の 1番(a)が組になりました。
j = 2 → data[1 + 2 - 1] = data[2] と key[2]
data
1a
2b
3a
4b
5c
6a
7b
8c
key
1a
2b
3c
data の 2番(b)と、key の 2番(b)が組になりました。
j = 3 → data[1 + 3 - 1] = data[3] と key[3]
data
1a
2b
3a
4b
5c
6a
7b
8c
key
1a
2b
3c
data の 3番(a)と、key の 3番(c)が組になりました。
i を 2 にすると、3組そろって1つ右へ
data
1a
2b
3a
4b
5c
6a
7b
8c
key
1a
2b
3c
data[2 + j - 1] なので、組になる相手は 2番・3番・4番。key ごと右へずれました。i が置きはじめる番号、j がkey の中の何番目かです。
i = key を置きはじめる番号 / j = key の中の何番目か
ここで見ているのは式が何と何を組にするかだけです(実際に比べた結果がどうなるかは ③ で決まります)。式が言っているのは、key を i 番の位置に置いて、上下で見比べる、ということです。
② 外側の for ── i はどこまで動かせるか
i は key を置きはじめる番号だと分かりました。では、どこまで動かせるでしょうか。置けなくなるところを見ます。
i を大きくしていくと、どこで置けなくなるか
上=配列 data(枡の番号は要素番号)/下=配列 key を i 番に重ねたもの(枡の番号は j)。琥珀=組になる枡/赤い破線=data の外にはみ出す枡
i = 6 ── ちょうど収まる
data
1a
2b
3a
4b
5c
6a
7b
8c
key
1a
2b
3c
key の 3番の真上が data の 8番=最後の枡。ぴったり収まりました。
i = 7 ── はみ出す
data
1a
2b
3a
4b
5c
6a
7b
8c
key
1a
2b
3c
key の 3番の上に、data の枡がありません(data は 8番まで)。7番からは置けません。
置ける最後は 6番 ── data の 8 枡から、key の 3 枡ぶんを引いて +1
data が 8 枡、key が 3 枡。key の3枡ぶんの場所が要るので、置ける最後は 8 - 3 + 1 = 6 番です。これが出題に書いてある (lenData - lenKey + 1) の正体です。
上限は、式のまま持って進まずに、ここで数字にしておきます。1つずれると、あとの数え方が全部ずれます。
③ else ── そろわなかったら、内側だけ抜ける
比べた結果が偽のとき、else の下は αの行から始まる繰返し処理を終了する の1行です。α は内側の for に付けた名前なので、終わるのは内側だけ。この記事では、これを「打ち切り」と呼びます。
外側の for は endfor まで生きているので、そのまま続きます。i が1つ増えて、j はまた 1 から始まります。
最後の j まで届いた場合も同じです。result に番号を足したあと、内側の for がふつうに終わって、外側が次の i へ進みます。見つかっても、そこで止まりません。だから i はいつも 1 から 6 まで動きます。
i を 1 から 6 まで動かして、真になった回数を数える
数えるのは /*** β ***/ の行が真になった回数です。同じ行に来た回数ではありません(偽のときも来ます)。β は内側の for の中にあるので、数えるのは「上下を見比べた回数」のうち「そろったほう」だけ ── 位置の数でも、見つかった並びの数でもありません。
数えるのはこの中の1行です(〔プログラム〕からの抜粋)
for (j を 1 から lenKey まで 1 ずつ増やす) // α
if (data[i + j - 1] が key[j] と等しい) /*** β ***/
if (j が lenKey と等しい)
resultの末尾 に iの値 を追加する
endif
else
αの行から始まる繰返し処理を終了する
endif
endfor
※ 上の〔プログラム〕からの抜粋です(1字も変えていません)。
読み取った3つを、i = 1 から 6 まで当てはめます。以下、i のことを「開始位置」と呼びます(key を置きはじめる番号だからです)。
6つの開始位置を、1つずつ
緑=そろった(β が真)/赤=そろわなかった(β が偽)/灰=打ち切ったので、比べずに終わった。「次へ」で開始位置を1つずつ進みます。
i = 1 ── 1番から重ねる
data
1a
2b
3a
4b
5c
6a
7b
8c
key
1a
2b
3c
a=a 真、b=b 真、a と c はそろわない → 打ち切り。真は 2 回
i = 2 ── 2番から重ねる
data
1a
2b
3a
4b
5c
6a
7b
8c
key
1a
2b
3c
1つ目から b と a でそろわない → 打ち切り。真は 0 回
i = 3 ── 3番から重ねる
data
1a
2b
3a
4b
5c
6a
7b
8c
key
1a
2b
3c
3つともそろいました。j が lenKey(3)まで来たので、result に 3 を追加します。真は 3 回
i = 4 ── 4番から重ねる
data
1a
2b
3a
4b
5c
6a
7b
8c
key
1a
2b
3c
b と a でそろわない → 打ち切り。真は 0 回
i = 5 ── 5番から重ねる
data
1a
2b
3a
4b
5c
6a
7b
8c
key
1a
2b
3c
c と a でそろわない → 打ち切り。真は 0 回
i = 6 ── 6番から重ねる
data
1a
2b
3a
4b
5c
6a
7b
8c
key
1a
2b
3c
3つともそろいました。result に 6 を追加します。真は 3 回
開始位置ごとの、真になった回数 ── 2・0・3・0・0・3
回数を決める
数えていたのは、この1行が真になった回数でした。
数えた行(〔プログラム〕からの抜粋)
if (data[i + j - 1] が key[j] と等しい) /*** β ***/
※ 上の〔プログラム〕からの抜粋です(1字も変えていません)。
開始位置ごとに出した数を、そのまま足します。
β が真となる回数
2 + 0 + 3 + 0 + 0 + 3 = 8 回
足しているのは真になった回数だけです。β の行に来たのは 12 回(真8 + 偽4)ですが、そろわなかった 4 回は入れません。設問が訊いているのは「条件式が真となる回数」だからです。
本番の手順 ── 「何回」を訊かれたら
- 何を数えるのかを、設問の文から1つに絞る。
「条件式が真となる回数」なら、偽は数えません。行を通った回数でも、ループが回った回数でもありません。ここを取りちがえると、全部数え直しになります。
- くり返しの上限を、先に数字にする。
この問題なら
lenData - lenKey + 1を 6 と出しておきます。上限を式のまま持って進むと、必ずどこかで1つずれます。 - 途中で抜ける行があるかを探す。
あれば、「1周あたり何回」が周ごとに変わります。かけ算で片づけられないので、1周ずつ数えるしかありません。
ここで初めて解答群を見る
数えた答えは 8。解答群で 8 は ク です。
答え合わせ ── 通して数え直す
手で数えたときに数え落としが無かったかを、頭から通して確かめます。β の行に来るたびに1コマ進み、真になったときだけ右端の数が増えます。数える行ではない if (j が lenKey と等しい) は、コマを分けていません(β が真のたびに通ります)。
1回ずつ追う β の行を通る12回
出典:令和7年度 科目B 公開問題 問4
ループ開始前
配列
いま計算していること
変数の状態
return で返す値
トレース表(進めると1行ずつ積み上がります)
正解は ク
β の条件式が真となる回数は 8 回。
解答群にある 6・2・4 は、どれも別の行を数えた値でした(外側の for/見つかった数/偽になった回数)。数えるのは /*** β ***/ の行が真になったときだけです。
同じ考え方で解く練習問題
同じプログラムに、別の呼び出しを与えます。key の長さも変わります。
当サイトで作った類題です(IPAの出題ではありません)
上と同じ search を search({"a", "b", "a", "a", "b"}, {"a", "b"}) として呼び出すと、/*** β ***/ の行の条件式が真となる回数は何回ですか。
「何回」を訊く問題を見分ける
解答群が数字だけの回は、見比べても何も決まりません。数えるしかない、という合図です。
そのときいちばん効くのが、上の「本番の手順」の3つ目です。途中で抜ける行(繰返し処理を終了する)があると、1周あたりの回数が周ごとに変わります。この1行を見落とすと、6 × 3 = 18 のようなかけ算で答えを作ってしまい、解答群に無い数が出てきます。
次に読む
この記事で引用した資料
いずれも独立行政法人情報処理推進機構(IPA)が公表したものです。IPAは公表済みの試験問題について、教育目的での使用に許諾および使用料を不要としていますが、著作権は放棄していません。本記事では問題文を改変せずに引用しています。
「search({“a”,”b”,”a”,”a”,”b”}, {“a”,”b”}) のときの回数を問う練習問題」は当サイトのオリジナルです。

