[Problem F] 古い記憶 良さそうな方法は思いつかなかった アイディア募集中!!! かなり強引に解いている 各判定データに対して 30 秒程度かかる 元の文章と改ざん文章の関係を考える ウィルス感染の可能性は高々 2 回であり 各々の感染では 1 文字削除 1 文字追加 1 文字変更が行われ

Similar documents
文字数は1~6なので 同じ本数の枝を持つパスで生成される呪文の長さは最大で6 倍の差がある 例えば 上図のようなケースを考える 1サイクル終了した時点では スター節点のところに最強呪文として aaaaaac が求まる しかしながら サイクルを繰り返していくと やがてスター節点のところに aaaaaa

プログラム例 /* ACM-ICPC2009 国内予選 Problem C */ // // filename = pc1.c // コンパイル

Taro-2分探索木Ⅱ(公開版).jtd

[Problem D] ぐらぐら 一般に n 個の物体があり i 番目の物体の重心の x 座標を x i, 重さを w i とすると 全体の n 重心の x 座標と重さ w は x = ( x w ) / w, w = w となる i= 1 i i n i= 1 i 良さそうな方法は思いつかなかった

Taro-リストⅠ(公開版).jtd

Taro-スタック(公開版).jtd

Taro-リストⅢ(公開版).jtd

Taro-再帰関数Ⅱ(公開版).jtd

ープのロープ長以下であれば実現可能である ケース 3: 3 本のロープの杭の位置を点 P 1 = (x 1, y 1, 0), 点 P 2 = (x 2, y 2, 0), 点 P 3 = (x 3, y 3, 0) とする 点 P 1 = (x 1, y 1, 0), 点 P 2 = (x 2,

Microsoft PowerPoint - 05.pptx

memo

Taro-再帰関数Ⅲ(公開版).jtd

Prog1_10th

memo

Taro-2分探索木Ⅰ(公開版).jtd

プログラミング基礎

PowerPoint プレゼンテーション

Microsoft PowerPoint - kougi10.ppt

Microsoft Word - no206.docx

PowerPoint プレゼンテーション

プログラミング方法論 II 第 14,15 回 ( 担当 : 鈴木伸夫 ) 問題 17. x 座標と y 座標をメンバに持つ構造体 Point を作成せよ 但し座標 は double 型とする typedef struct{ (a) x; (b) y; } Point; 問題 18. 問題 17 の

Microsoft Word - 13

Microsoft PowerPoint - algo ppt [互換モード]

バイオプログラミング第 1 榊原康文 佐藤健吾 慶應義塾大学理工学部生命情報学科

Microsoft Word - no202.docx

PowerPoint プレゼンテーション

PowerPoint プレゼンテーション

PowerPoint プレゼンテーション

memo

< F2D837C E95CF CF68A4A94C5816A2E6A>

Microsoft PowerPoint - 5Chap15.ppt

Microsoft Word - no15.docx

Microsoft Word - 3new.doc

第2回

次に示す数値の並びを昇順にソートするものとする このソートでは配列の末尾側から操作を行っていく まず 末尾の数値 9 と 8 に着目する 昇順にソートするので この値を交換すると以下の数値の並びになる 次に末尾側から 2 番目と 3 番目の 1

始めに, 最下位共通先祖を求めるための関数 LcaDFS( int v ) の処理を記述する. この関数は値を返さない再帰的な void 関数で, 点 v を根とする木 T の部分木を深さ優先探索する. 整数の引数 v は, 木 T の点を示す点番号で, 配列 NodeSpace[ ] へのカーソル

コマンドラインから受け取った文字列の大文字と小文字を変換するプログラムを作成せよ 入力は 1 バイトの表示文字とし アルファベット文字以外は変換しない 1. #include <stdio.h> 2. #include <ctype.h> /*troupper,islower,isupper,tol

PowerPoint プレゼンテーション

Microsoft Word - no12.doc

二分木の実装

PowerPoint プレゼンテーション

C プログラミング演習 1( 再 ) 2 講義では C プログラミングの基本を学び 演習では やや実践的なプログラミングを通して学ぶ

Microsoft Word - no11.docx

文法と言語 ー文脈自由文法とLR構文解析2ー

情報処理Ⅰ

memo

第2回

Microsoft PowerPoint - 06.pptx

gengo1-8

memo

1. 入力した正の整数を降順に並べ換えて出力するプログラムを作成せよ プログラムは個別にコンパイルし make コマンドで実行すること 入力データは 50 以下とし 以下の数が混在しているとする 16 進数 : 先頭 1 文字が x または X( エックスの小文字か大文字 ) 8 進数 : 先頭 1

Microsoft PowerPoint - kougi11.ppt

Microsoft PowerPoint pptx

Taro-最大値探索法の開発(公開版

Microsoft PowerPoint - lec10.ppt

講習No.9

講習No.12

Microsoft Word - Cプログラミング演習(11)

Microsoft Word - no103.docx

Microsoft Word - Cプログラミング演習(9)

PowerPoint Presentation

Microsoft Word - no205.docx

本サンプル問題の著作権は日本商工会議所に帰属します また 本サンプル問題の無断転載 無断営利利用を厳禁します 本サンプル問題の内容や解答等に関するお問 い合わせは 受け付けておりませんので ご了承ください 日商プログラミング検定 STANDARD(C 言語 ) サンプル問題 知識科目 第 1 問 (

今までの復習 プログラムで最低限必要なもの 入力 ( キーボードから ファイルから ) 出力 ( 画面へ ファイルへ ) 条件分岐 : 条件の成立 不成立により 異なる動作をする 繰り返し : 一定の回数の繰返し 条件成立の間の繰返し 関数の定義 関数の呼び出し C ではそれ以外に ポインタ データ

memo

RX ファミリ用 C/C++ コンパイラ V.1.00 Release 02 ご使用上のお願い RX ファミリ用 C/C++ コンパイラの使用上の注意事項 4 件を連絡します #pragma option 使用時の 1 または 2 バイトの整数型の関数戻り値に関する注意事項 (RXC#012) 共用

Prog1_15th

Microsoft PowerPoint - 計算機言語 第7回.ppt

プログラミングI第10回

ポインタ変数

データ構造

Microsoft Word - 第5回 基本データ構造2(連結リスト).doc

Microsoft Word - Cプログラミング演習(12)

PowerPoint Presentation

Microsoft Word - no13.docx

PowerPoint Presentation

Microsoft Word - ICPC2014_DomG.docx

PowerPoint Template

program7app.ppt

kiso2-09.key

02: 変数と標準入出力

新・明解C言語 ポインタ完全攻略

Microsoft PowerPoint - kougi9.ppt

02: 変数と標準入出力

4 分岐処理と繰返し処理 ( 教科書 P.32) プログラムの基本的処理は三つある. (1) 順次処理 : 上から下に順番に処理する ぶんきそろ (2) 分岐処理 : 条件が揃えば, 処理する はんぷく (3) 反復処理 : 条件が揃うまで処理を繰り返す 全てのプログラムは (1) から (3) の

Prog1_6th

第1回 プログラミング演習3 センサーアプリケーション

C 言語第 3 回 2 a と b? 関係演算子 a と b の関係 関係演算子 等しい a==b 等しくない a!=b より大きい a>b 以上 a>=b より小さい a<b 以下 a<=b 状態 真偽 値 条件が満たされた場合 TRUE( 真 ) 1(0 以外 ) 条件が満たされなかった場合 F

<4D F736F F D20438CBE8CEA8D758DC F0939A82C282AB2E646F63>

r08.dvi

Microsoft PowerPoint - CproNt02.ppt [互換モード]

Microsoft Word - Cプログラミング演習(10)

Java講座

02: 変数と標準入出力

ohp08.dvi

02: 変数と標準入出力

Taro-ポインタ変数Ⅰ(公開版).j

gengo1-12

PowerPoint Presentation

データ構造

Transcription:

[Problem F] 古い記憶 良さそうな方法は思いつかなかった アイディア募集中!!! かなり強引に解いている 各判定データに対して 30 秒程度かかる 元の文章と改ざん文章の関係を考える ウィルス感染の可能性は高々 2 回であり 各々の感染では 1 文字削除 1 文字追加 1 文字変更が行われる 削除と追加は双対な関係であるので 改ざん文章に対して 改ざん文章そのもの ( 実際にはウィルス感染が起こっていなかった ) 改ざん文章から任意の1 文字を削除 ( 元の文章に対しては1 文字追加が発生 ) 改ざん文章の任意の1 文字を変更 ( 元の文章に対して1 文字変更が発生 ) 改ざん文章の任意の場所に任意の文字を追加 ( 元の文章に対しては1 文字削除が発生 ) 改ざん文章に対して任意の2 文字を削除 改ざん文章に対して任意の2 文字を変更 改ざん文章に対して任意の2 文字を任意の位置に追加 改ざん文章に対して1 文字削除と1 文字追加 改ざん文章に対して1 文字削除と1 文字変更 改ざん文章に対して1 文字変更と1 文字追加により元の文章の候補を生成する 候補の生成において 文字の変更や追加に対しては 該当部分の文字を? としておく このような? を含む文章に対して 文章の各位置が少なくとも1 個のピースでカバーされることをチェックする? を一つも含まない場合は 候補文章に対して 各ピースが部分文字列として現れるかをチェックし 候補文章の全ての位置が少なくとも一つ以上のピースの部分文字列となっていれば候補として採用する? を含む場合は? ごとに まず? に代入することによりその部分があるピースの部分文字列となる可能性がある文字のリストを求める その後? にそれらの文字を順次代入して? を含まない場合と同様の判定を行う なお 同じ候補文字列を複数回チェックするのをさけるため チェックした文字列は2 分探索木に登録しておく また 最終的に候補となる文章の個数が5 以下の場合は その文章を辞書式順にプリントする必要があるので 候補となることが分かった文章も2 分探索木に登録している プログラム例 // ACM-ICPC 2010 Japan Online Contest Problem F // http://icpc2010.honiden.nii.ac.jp/domestic-contest/problems#section_f // ファイル名 : pf.c // コンパイル方法 : cc pf.c // 実行方法 :./a.out < F0 > F0.result など 1

// チェック方法 : diff F0.ans F0.result など // // アルゴリズム概略 // 改ざん文章に最大 2 回の文字削除 / 変更 / 挿入を行ったものが候補文章となりうる // 変更や挿入に対し その位置の文字を '?' で表し これらの操作により得られる // 文字列を作成 各ピースに対して そのピースが '?' 部分をカーバーできる可能性を // 探り 可能性がある場合にその '?' が取りうる文字のリストを作成する // '?' 位置に候補となりうる文字を代入した文字列を作成し その文字列の各文字が // 少なくとも一つのピースによりカバーされているかをチェックする // なお文字の削除しか行わない場合や そもそも感染していない場合は チェック対象の // 文章に '?' は含まれないので この文章の各文字が少なくとも一つのピースにより // カバーされているかどうかを判定すればよい // 同じ文字列を繰り返しチェックするのをさけるため チェック済みの文字列は // 二分探索木で管理する また 候補となる文字列も 2 分探索木で管理する // 後者の 2 分探索木を in order で出力すれば候補文字列を辞書順に出力することができる #include <stdio.h> #include <string.h> #include <stdlib.h> #define MAX_N 30 #define MAX_SLEN 42 #define MAX_PLEN 20 // ピースの数の最大値 // 元の文章の長さの最大値 = 改ざん文章の長さの最大値 +2 // ピースの長さの最大値 struct node_type // 2 分探索木の節点の構造体 char *sentence; // 登録する文字列 struct node_type *left; // 左側の子供へのポインタ struct node_type *right; // 右側の子供へのポインタ ; typedef struct node_type node_t; // 上記の構造体の型を node_t と名付ける node_t *candidate; node_t *checked; // 候補となる文章を格納するための 2 分探索木 // チェックした文字列を格納するための 2 分探索木 char pieace[max_n][max_plen+1]; // ピース文字列を格納する配列 char original[max_slen+1]; // 元の文章を格納する配列 char altered[max_slen+1]; // 改ざん文章を格納する配列 char ans[5][max_slen+1]; // 可能な元の文章を格納する配列 int used[128]; // ピースの中に現れる文字を示す配列 // used[c]==1 ---> 文字 c がピースに含まれる // used[c]==0 ---> 文字 c はピースに含まれない char chused[27]; // ピースに含まれている文字のリスト char chused1[27],chused2[27]; int d; // 感染回数の最大値 int n; // ピースの数 int nc; // ピースに現れる文字の種類数 int c; // もとの文章の候補数 int nc1, nc2; void clean_bst(node_t *pt); 2

int print_bst(node_t *pt, int count); void check0(char *str); void gen_candc(char *str, int p1, int p2); void check(char *str); void check_del(char *str, int level); void check_chg(char *str, int level); void check_ins(char *str, int level); void solve(); // 2 分探索木に文字列を登録する // str = 登録する文字列, ppt = 2 分探索木の根節点へのポインタを格納している場所 // mode = 1 ---> 候補となる文字列の登録 // 戻り値 : 0 ---> 新規登録成功 1 ---> 既に登録されていた int ins_bst(char *str, node_t **ppt, int mode) node_t *pt; int sc; node_t *root; root = *ppt; // root -> 2 分探索木の根節点 if(root == NULL) // 2 分探索木が空の場合 root = (node_t *)malloc(sizeof(node_t)); // 2 分探索木の節点を格納する領域を確保 root->left = root->right = NULL; // この新しい節点は左右の子を持たない root->sentence = (char *)malloc(strlen(str)+1); // この節点に文字列を格納する領域を確保 strcpy(root->sentence, str); // 確保した領域に文字列をコピー if(mode) c++; // 候補文字列を登録した場合は候補数を 1 増やす *ppt = root; // この節点を 2 分探索木の根節点とする return 0; // 新規登録成功 pt = root; // pt -> 2 分探索木の根節点 while(pt) // pt が NULL で無い間以下を繰り返す sc = strcmp(str, pt->sentence); // 今回登録する文字列と // pt が指す節点に登録されている文字列を比較 if(sc < 0) // 今回登録する文字列の方が小さく if(pt->left) // 左の子が存在するなら pt = pt->left; // pt を左の子へのポインタとする else break; // 左の子が存在しない場合は 繰り返しを終了 else if (sc == 0) // 今回登録する文字列がこの節点に登録済なら return 1; // 1を返す ( 登録済 ) else // 今回登録する文字列の方が大きく if(pt->right) // 右側の子が存在するなら pt = pt->right; // pt を右の子へのポインタとする else break; // 右の子が存在しない場合は 繰り返しを終了 if(sc<0) // 今回登録する文字列の方が小さく左の子が存在しない場合 pt->left = (node_t *)malloc(sizeof(node_t)); // 左の子を格納する領域を確保し pt = pt->left; // pt を左の子へのポインタとする 3

else // 今回登録する文字列の方が大きく右の子が存在しない場合 pt->right = (node_t *)malloc(sizeof(node_t)); // 右の子を格納する領域を確保し pt = pt->right; // pt を右の子へのポインタとする pt->left = pt->right = NULL; // pt が指す新しい節点は左右の子は持たない pt->sentence = (char *)malloc(strlen(str)+1); // その節点に登録する文字列を格納する // 領域を確保 strcpy(pt->sentence, str); // 確保した領域へ文字列をコピー if(mode) c++; // 候補文字列の登録なら 候補数を 1 増やす return 0; // 新規登録成功 void clean_bst(node_t *pt) if(pt) free(pt->sentence); clean_bst(pt->left); clean_bst(pt->right); free(pt); // 2 分探索木が使用していた領域を解放 // pt が指す節点の文字列を格納していた領域を解放 // 左の子以下を再帰的に post order 順に解放 // 右の子以下を再帰的に post order 順に解放 // 最後にこの節点自体の領域を解放 (post order) // pt が指す節点を根節点とする 2 分探索木に登録されている文字列を // count 個辞書順 (inorder) に印刷 戻り値は残り印刷個数 int print_bst(node_t *pt, int count) int pn; if(count==0) return 0; // count が0なら何もしない if(pt==null) return count; // pt が NULL なら count を返す count = print_bst(pt->left, count); // まず左の子以下を再帰的に in order で // count 個印刷 if(count == 0) return 0; // 残り印刷数が 0 なら終了 printf("%s n", pt->sentence); // この節点に登録されている文字列を印刷 count--; // count を 1 減らす count = print_bst(pt->right,count); // 右の子以下を再帰的に in order で // count 個印刷 return count; // 残り印刷数を返す // 文字列 str が 元の文章の候補となりうるか判定し // 候補になる場合は 候補文字列を格納する二分探索木に登録し // 候補数を 1 増やす void check0(char *str) int cover[max_slen]; // 文字列 str の各文字がピース文字列により // カバーされているかを調べるための作業用配列 int i,j,len,lenp; char *cp, *pos; len = strlen(str); // 文字列 str の長さ for(i=0; i<len; i++) cover[i] = 0; // 作業用配列を0で初期化 // ( どの文字もカバーされていない ) 4

for(i=0; i<n; i++) cp = str; // cp: 最初は与えられた文字列 (str) 全体 lenp = strlen(pieace[i]); // i 番目のピース文字列の長さ while(*cp) // cp が NULL で無い間以下を繰り返す pos = strstr(cp, pieace[i]); // 文字列 cp の中に i 番目のピースが部分文字列として if(pos == NULL) break; // 含まれていなければ // i 番目のピースに対する処理終了 for(j=pos-str; j<pos-str+lenp; j++) cover[j]=1; // i 番目のピースに一致する部分を 1 にセット cp = pos+1; // 一致した位置の次の場所から再度一致するかを繰り返しチェック for(i=0; i<len; i++) if(cover[i] == 0) return; // カバーされていない文字が 1 文字でもあれば終了 ins_bst(str, &candidate, 1); // カバーされているので // 候補文字列として二分探索木に登録 // '?' の位置 ( 最大 2 か所 ) に入りうる文字のリストを作成 // p1 は最初の '?' の位置 p2 は 2 番目の '?' の位置 void gen_candc(char *str, int p1, int p2) int used1[128], used2[128]; // used1[c]==1 ---> 最初の '?' の位置に文字 c が入りうる // used2[c]==1 ---> 二番目の '?' の位置に文字 c が入りうる int i,j,k; int lens, lenp; for(i=0; i<128; i++) used1[i] = used2[i] = 0; // 配列 used1 と used2 を0で初期化 lens = strlen(str); // lens == 文字列 str の長さ for(i=0; i<n; i++) // 各ピースに対して char *cp; cp = pieace[i]; // cp = i 番目のピース lenp = strlen(cp); // lenp = i 番目のピースの長さ for(j=0; j<=lens-lenp; j++) char q1, q2; q1 = q2 = 0; for(k=0; k<lenp; k++) if(str[j+k] == cp[k]) continue; if(str[j+k] == '?') // str の j 番目から '?' の手前まで i 番目のピースと一致 if(j+k == p1) q1=cp[k]; // 最初の '?' なら // その位置に対応するピースの文字を q1 に else q2=cp[k]; // 2 番目なら q2 に入れる continue; else break; // 一致しない場合はそのピースに対する処理終了 if(k==lenp) // ピースの最後まで一致したら if(q1) used1[q1] = 1; // q1 を最初の '?' に利用できる文字としてマーク if(q2) used2[q2] = 1; // q2 を二番目の '?' に利用できる文字としてマーク 5

// 利用可としてマーク付けされた文字をリストの形に変換 j = k = 0; for(i='.'; i<='z'; i++) if(used1[i]) chused1[k++]=i; // 最初の '?' に利用できる文字のリストを構成 if(used2[i]) chused2[j++]=i; // 二番目の '?' に利用できる文字のリストを構成 nc1 = k; // 最初の '?' に利用できる文字のリストの長さ nc2 = j; // 二番目の '?' に利用できる文字のリストの長さ // '?' を含む ( 最大 2 か所 ) 文字列が候補文章となる可能性を判定 void check(char *str) char work[max_slen+1]; int i,j,len; int nq, q1, q2; int c1,c2; if(ins_bst(str, &checked,0)) return; // 既にこの文字列をチェック済みなら何もしない len = strlen(str); // len = 文字列の長さ nq = 0; // nq は '?' の個数 q1 = q2 = -1; // '?' の位置を見つける for(i=0; i<len; i++) if(str[i] == '?') // '?' が見つかったので nq++; // nq を1 増やす if(nq==1) q1=i; // 最初の '?' ならその位置を q1 に else q2 = i; // 2 回目の '?' ならその位置を q2 に格納 break; switch(nq) case 0: // '?' が無い場合 check0(str); // '?' を含まない文字列のチェックを呼び出す break; case 1: // '?' が 1 個ある場合 gen_candc(str, q1, q2); // '?' の位置に入る可能性がある文字のリストを作成 c1 = str[q1]; // '?' を退避 for(i=0; i<nc1; i++) str[q1] = chused1[i]; // '?' の位置に可能性のある文字を順次代入し check0(str); // 候補文章となるかどうかを判定 str[q1] = c1; // q1 番目の文字を '?' に戻す break; case 2: // '?' が 2 個ある場合 ( 場所は q1 と q2) gen_candc(str, q1, q2); // '?' の位置に入る可能性がある文字のリストを作成 c1 = str[q1]; // '?' を退避 c2 = str[q2]; // '?' を退避 for(i=0; i<nc1; i++) str[q1] = chused1[i]; // 最初の '?' の位置に可能性のある文字を順次代入し for(j=0; j<nc2; j++) 6

str[q2] = chused2[j]; // 更に二番目の '?' の位置にも // 可能性のある文字を順次代入し check0(str); // 候補文章となるかどうかを判定 str[q1] = c1; // q1 番目の文字を '?' に戻す str[q2] = c2; // q2 番目の文字を '?' に戻す // 文字を削除した文章が候補文章となる可能性を判定 // level==1 ---> まず 1 文字削除した文章の可能性判定を行い // d==2 なら 更にもう 1 文字削除 / 変更 / 挿入した場合の可能性も判定 // level==2 ---> 1 文字削除した文章の可能性を判定 // ( 既に 1 文字削除した状態で呼び出される ) void check_del(char *str, int level) int i,j,len; char *cp; char work[max_slen+1]; // 文字を削除するために使用する作業用配列 len = strlen(str); // len = 文字列の長さ for(i=0; i<len; i++) // 各 i = 0 ~ len-1 に対して cp = work; for(j=0; j<len; j++) // i 番目の文字を削除した文字列を work に作成し if(j==i) continue; *cp++ = str[j]; *cp = 0; check(work); // それが候補文章となる可能性を判定 if(d==2 && level==1) // もし感染の可能性が 2 回あり 今の削除が 1 回目なら check_del(work, 2); // もう 1 回削除するケースと check_chg(work, 2); // 変更するケースと check_ins(work, 2); // 挿入するケースについて可能性を判定 // 文字を変更した文章が候補文章となる可能性を判定 // level==1 ---> まず 1 文字変更した文章の可能性判定を行い // d==2 なら 更にもう 1 文字変更 / 挿入した場合の可能性も判定 // level==2 ---> 1 文字変更した文章の可能性を判定 // ( すでに 1 文字削除 / 変更した状態で呼び出される ) void check_chg(char *str, int level) int i,j,k,len; char *cp, work[max_slen+1]; // 文字を変更するために使用する作業用配列 char cc; len = strlen(str); // len = 文字列の長さ strcpy(work,str); // まず作業用配列へ文字列をコピー for(i=0; i<len; i++) // 各 i = 0 ~ len-1 に対して cc = work[i]; // i 番目の文字を cc に退避し work[i] = '?'; // その位置の文字を '?' として 7

check(work); if(d==2 && level==1) check_chg(work, 2); check_ins(work, 2); work[i] = cc; // その文字列が候補文章となる可能性を判定 // もし感染の可能性が 2 回あり今の変更が 1 回目なら // もう 1 回変更するケースと // 1 文字挿入するケースについて可能性を判定 // i 番目の文字を元の文字に戻す // 文字を挿入した文章が候補文章となる可能性を判定 // level==1 ---> まず 1 文字挿入した文章の可能性判定を行い // d==2 ならさらにもう 1 文字挿入した文章の可能性も判定 // level==2 ---> 1 文字挿入した文章の可能性を判定 // ( 既に 1 文字削除 / 変更 / 挿入した状態で呼び出される ) void check_ins(char *str, int level) int i,j,k,len; char *cp; char work[max_slen+1]; // 文字を挿入するために使用する作業用配列 len = strlen(str); // len = 文字列の長さ for(i=0; i<=len; i++) // 各 i = 0 ~ len に対して k=0; for(j=0; j<=len; j++) // i 番目に 1 文字 (?) 挿入した文字列を作成し if(i==j) work[j] = '?'; else work[j] = str[k++]; work[len+1] = 0; check(work); // その文字列が候補文章となる可能性を判定 if(d==2 && level==1) // もし感染の可能性が 2 回あり今挿入したのが 1 回目なら check_ins(work, 2); // level=2 で再度 check_ins を呼び出す void solve() int i,j; char *cp; for(i=0; i<128; i++) used[i]=0; // ピース中に現れる文字を示す配列を初期化 for(i=0; i<n; i++) // 各ピースに対して cp = pieace[i]; // cp -> ピース文字列 while(*cp) // ピース文字列の各文字に対して used[*cp++] = 1; // 配列 used のその文字に対応する位置に1を代入 nc = 0; // nc はピースに現れる文字の種類数 for(i=0; i<128; i++) if(used[i]) chused[nc++] = i; // 配列 chused にピースに現れる文字のリストを構築 c = 0; // 元の文章の可能性の数を 0 に初期化 candidate = checked = NULL; // 候補とチェック済みの文字列を格納する // 空の 2 分探索木を作成 check(altered); // ウイルスに 1 度も感染しなかったケースがありうるかチェック 8

check_del(altered, 1); // 改ざん文章から 1 文字消去した文章の可能性をチェック check_chg(altered, 1); // 改ざん文章に対し 1 文字変更した文章の可能性をチェック check_ins(altered, 1); // 改ざん文章に 1 文字挿入した文章の可能性をチェック printf("%d n",c); // 候補の数を出力 if(c <= 5) print_bst(candidate, c); // それが 5 以下の場合は 候補を辞書順で出力 clean_bst(checked); // チェック済み文字列を格納していた二分探索木の領域を 解放 clean_bst(candidate); // 候補の文字列を格納していた二分探索木の領域を解放 int main() int i; while(1) scanf("%d %d", &d, &n); if(d==0 && n==0) break; scanf("%s",altered); for(i=0; i<n; i++) scanf("%s", pieace[i]); solve(); // d と n の入力 // 両方とも0なら終了 // 改ざん文章の入力 // ピースを n 個入力 // 解を求める 9