1220-The Devil of Gravity
問題概要
悪魔のエディタを作った男が居た.
悪魔のエディタは次の独特の機能を持っている.
- 文字列(空白を持たない連続した文字の並び)の下に何も文字列が無い場合,その文字列は落下する.下に文字列がある状態になるか,一番下に辿り付くまで落下は継続する.
- 同じ行にある二つの文字列が隣接した場合は,連結して一つの文字列となる.
例えば,
abcdef ghijkl mnop qrstuvw
から,mnopを消去すると
abcdef qrstuvw ghijkl
のようになる.
これの一番下の行の二つの文字列の間にxを加えると
abcdef qrstuvwxghijkl
となる.
一般に,何かコマンドを実行したら,次のルールが適用される.
ただし,Sは文字列とし,left(S)は文字列の左端のカラム番号,right(S)は文字列の右端のカラム番号
,そしてrow(S)はSのある行の番号(下からの番号)とする.
- row(S)-1行目のleft(S)からright(S)の間に文字列が無い場合,Sはrow(S)-1行目に移動する.これは条件を満たさなくなるか,row(S)=0になるまで実行される.
- 二つの文字列S,Tが同じ行にあり,right(S)+1=left(T)の場合二つの文字列は連結され,一つの文字列になる.もちろん行は移動したりしない.
文字列はかならず,一つ以上の文字を含むことと,最初のルールの適用が完全に終わった後で2番目のルールが適用されることに気を付けろ.
例えば
dddddddd cccccccc bbbb aaa
において,bbbbの右端のbを消去した場合,"bbb"と"ccccccccc"が連結することはなく,"aaa"と"ccccccccc"が連結される.
悪魔のエディタにはカーソルが存在し,それは常に一つの文字列(これを現在の文字列という)を差す.カーソルは文字列のどれかの文字か,文字列の末尾を差す.カーソルは文字列を支えるのに使えないことに注意せよ.例えば,先程の例のbbbbの一番最後にカーソルが居たとしても結果は変わらず,最終的にdの左端を差すことになる.
悪魔のエディタは以下の1文字の英数字で表わされるコマンドを受け付ける
- F カーソルを一つ後ろに進める.もし,文字列の末尾を差していた場合エラーとなる.
- B カーソルを一つ前に進める.もし,文字列の先頭を差していた場合エラーとなる.
- P カーソルを一つ上に進める.カーソルの位置が不正になった場合はエラーとなる.
- N カーソルを一つ下に進める.カーソルが一番下の行に入るか,カーソルの位置が不正になった場合エラーとなる.現在の文字列の始めもカーソルの位置も変化しません.現在の文字列が空になる場合,一つ下の行に移動します.
- D カーソルの位置にある文字を消去する.もし,カーソルが末尾を差している場合エラーとなる.その文字を消したことにより,現在の文字列が落下するならば,カーソルも一緒に落下する.現在の文字列の全てが消える場合カーソルは一つ下に落下する.
- C 一文字の文字列を現在のカーソルの上に作成する.その文字は現在カーソルが差している文字となる.もし,カーソルが末尾を差している場合エラーとなる.また,既に文字列が存在した場合もエラーとなる.コマンド実行後カーソルは上に1つ,右に1つ進行する.
- 小文字英字と数字が入力された場合,現在のカーソルの位置にその文字を挿入する.カーソルは一文字後ろに移動し,文字列の最初のカラムが変化することは無い.
エラーが起きた場合は,そのままエディタは不正終了する.
あなたの仕事はこの悪魔のエディタを実装することである.
入力
最初の行にテストケースの個数が与えられる.
各テストケースは一行からなり,
コマンドの文字列(100文字以下)が与えらえる.
コマンドの最初は初期の文字列からなり,この文字列の末尾をカーソルは差すことになる.
出力
各テストケースについて,
現在の文字列を一行に出力せよ.途中でエラーが発生した場合は"ERROR"と出力せよ.
解法
実装する
実装(C++)
#include <cstring> #include <cstdio> #include <algorithm> #include <iostream> using namespace std; char MAP[201][201]; int curX,curY; string cmd; bool hasError=false; const int W=201,H=10; #define REP(i,x)for(int i=0;i<(int)x;i++) void init(){ curX=0,curY=0; memset(MAP,' ',sizeof(MAP)); hasError=false; } void show(){ cout<<"----"<<endl; cout<<curX<<","<<curY<<endl; for(int y=H-1;y>=0;y--){ REP(x,W){ cout<<MAP[x][y]; } cout<<endl; } } void addChar(char t){ int x=curX; while(MAP[x][curY]!=' ')x++; for(int tx=x;tx>curX;tx--){ MAP[tx][curY]=MAP[tx-1][curY]; } MAP[curX][curY]=t; curX++; } void forward(){ if(MAP[curX][curY]==' ')hasError=true; curX++; } void back(){ if(curX==0||MAP[curX-1][curY]==' ')hasError=true; curX--; } bool isOk(int x,int y){ if(MAP[x][y]!=' '||(x>0&&MAP[x-1][y]!=' '))return true; return false; } //一つ上に進める void P(){ if(isOk(curX,curY+1)){ curY++; }else{ hasError=true; } } void N(){ if(curY>0&&isOk(curX,curY-1)){ curY--; }else{ hasError=true; } } void createNewColumn(){ if(MAP[curX][curY]==' '){ hasError=true; } if(MAP[curX][curY+1]!=' '){ hasError=true; } MAP[curX][curY+1]=MAP[curX][curY]; curX++; curY++; } void D(){ if(MAP[curX][curY]==' '){ hasError=true;return; } //全部消えるかの判定 if(MAP[curX+1][curY]==' '){ if((curX>0&&MAP[curX-1][curY]==' ')||curX==0){ MAP[curX][curY]=' '; curY--; if(curY<0){ hasError=true; } }else{ int tX=curX; while(MAP[tX][curY]!=' '){ MAP[tX][curY]=MAP[tX+1][curY]; tX++; } } }else{ int tX=curX; while(MAP[tX][curY]!=' '){ MAP[tX][curY]=MAP[tX+1][curY]; tX++; } } } bool can(int sx,int ex,int y){ for(int x=sx;x<ex;x++){ if(MAP[x][y]!=' ')return false; } return true; } void word_down(){ bool update=false; do{ update=false; for(int y=H-1;y>0;y--){ REP(x,W){ if(MAP[x][y]==' ')continue; int tx=x; while(MAP[tx][y]!=' ')tx++; int tar_y=y; for(int ty=y-1;ty>=0;ty--){ if(!can(x,tx,ty)){ break; }else{ tar_y=ty; } } if(tar_y!=y){ for(int qx=x;qx<tx;qx++){ MAP[qx][tar_y]=MAP[qx][y]; MAP[qx][y]=' '; } update=true; } x=tx-1; } } }while(update); } void solve(){ init(); REP(i,cmd.size()){ //cout<<cmd[i]<<endl; //show(); if(hasError)break; char now=cmd[i]; if(islower(now)||isdigit(now)){ addChar(now); }else{ switch(now){ case 'C': createNewColumn(); break; case 'F': forward(); break; case 'B': back(); break; case 'P': P(); break; case 'N': N(); break; case 'D': D(); break; default: break; } } word_down(); while(curY>0&&MAP[curX][curY]==' '&&(curX==0||MAP[curX-1][curY]==' '))curY--; } //show(); if(hasError){ cout<<"ERROR"<<endl; }else{ //cout<<":"; while(true){ if(curX&&MAP[curX][curY]!=' '&&MAP[curX-1][curY]==' ')break; if(curX==0)break; curX--; } while(MAP[curX][curY]!=' ')cout<<MAP[curX++][curY]; cout<<endl; } } int main() { int T; cin>>T; while(T--){ cin>>cmd; solve(); //show(); } return 0; }