以下の、paiza Bランク スキルチェック過去問「みんなでしりとり」をC言語で解きました。
解答へコメントを付与して、簡単に覚書しておきます。
解くのにだいぶ難航して、合計20回くらいデバッグ出力と誤答を繰り返しました。
真っ当に順当にしりとりを判定して、シミュレーションをするという戦略をとりました。
そのためか、ソースコードは長いです。
では、最終的なコードは以下の通りです。
#include <stdio.h>
#include <string.h>
int main(void){
// 入力受け取り(人数、キーワード数、発言数)
char buf[100];
int people_num, keyword, meme;
fgets(buf, sizeof(buf), stdin);
sscanf(buf, "%d %d %d", &people_num, &keyword, &meme);
// 入力受け取り(キーワード一覧)
char keywords[1000][100];
for(int i = 0; i < keyword; i++) {
fgets(keywords[i], sizeof(keywords[i]), stdin);
keywords[i][strlen(keywords[i])-1] = '\0';
//printf("%s\n", keywords[i]);
}
// 参加メンバーリスト作成(1でゲーム残留、0で敗退)
int people[10];
for(int i = 0; i < people_num; i++) {
people[i] = 1;
}
// ここからゲーム開始
int count = 0; // メンバーリストの現在地インデックス
char mae = '\0'; // しりとりのための最後尾文字
// 発言数分処理を繰り返す
for(int i = 0; i < meme; i++) {
int flag = 0; // ゲームへの残留/敗退判定フラグ(最初は敗退状態からはじめる)
char buf[100];
fgets(buf, sizeof(buf), stdin);
// 文字列の改行が邪魔なので、改行がついていたら、削除する(改行を文字列終端に置き換える)
if(buf[strlen(buf)-1] == '\n') {
buf[strlen(buf)-1] = '\0';
}
// 発言文字列がキーワード一覧に存在するか確認する
for(int j = 0; j < keyword; j++) {
//printf(">>%s %s<<\n",buf, keywords[j]);
if(strcmp(keywords[j], buf) == 0) {
flag = 1; // キーワードが存在したら、フラグを残留状態へ倒す
// キーワードの再使用は禁止なので、
// 1文字目を文字列終端に置き換えて、簡易的に使用キーワードを削除する
keywords[j][0] = '\0';
break;
}
}
// キーワードが存在したら、次に'z'で終わっている文字列かチェックする
if(flag == 1 && buf[strlen(buf)-1] == 'z') {
flag = 0; // zで終わっていたら敗退状態へ
}
//printf("[%c][%c]\n", mae, buf[0]);
// フラグが残留状態だったら、しりとり条件の判定をする
if(flag == 1) {
// 最後尾文字が\0でないなら、しりとり条件の判定が必要
if(mae != '\0') {
// 最後尾文字と新しい発言の冒頭文字が一致しなかったら、しりとり失敗
// フラグを敗退状態へ
if(mae != buf[0]) {
flag = 0;
}
}
}
//printf("%s %d %d\n", buf, count, flag);
// フラグが敗退状態だったら、メンバーリストに敗退を記録する
if(flag == 0) {
people[count] = 0;
mae = '\0'; // ゲームのしりとり条件をリセットするため、最後尾文字を\0にしておく
}
else {
mae = buf[strlen(buf)-1];
}
// 次の順番のメンバーを探索する
do {
// countをインクリメントして、人数をはみ出る場合はループで0に戻す
count++;
if(count >= people_num) {
count = 0;
}
// 残留メンバーが1人かどうかを判定し、1人だったらゲーム終了
int sum = 0;
for(int i = 0; i < people_num; i++) {
sum += people[i];
}
if(sum == 1) {
break;
}
} while(people[count] != 1); // 残留状態ではないメンバーだったら、再度ループ処理(次のメンバーのチェックへ)
}
// ここから結果を出力する
// 残留メンバーをカウントする
int result = 0;
for(int i = 0; i < people_num; i++) {
if(people[i] == 1) {
result++;
}
}
printf("%d\n", result);
// 残留メンバーリスト出力
for(int i = 0; i < people_num; i++) {
if(people[i] == 1) {
printf("%d\n", i+1);
}
}
return 0;
}
難航ポイント
難航したポイントは、
- キーワード、発言へ付与される改行が邪魔で、判定が正常にならない
- メンバーのターンの順番がループするのを忘れた
- 残留メンバーが1人になったときにwhileループをbreakするのを忘れて、無限ループを起こす
こんな感じでした。
終わりに
難航ポイントを踏まえると、もうちょっと慎重に実装することが必要だなと思いました。
部分正答みたいな回答が何度もあったため、条件の吟味が必要ですね。
特に0 -> 1 -> 2 -> 0 -> 1 -> ...みたいなインデックスのループ処理を忘れやすいです。
ここを今後は意識していきたいです