解いた問題のソースコードと解説など。


POJ 3510 A Tale from the Dark Side of the Moon

問題

文字列が一行ずつ与えられる。一行ごとに以下の処理を行って出力せよ。

  • アルファベット小文字とスペース以外は削除
  • "dd"という文字列があったら"p"に変換("ddd"という入力はないものとする)
  • "ei"という文字列があったら"ie"に変換(ただし直前の文字が'c'であれば変換しない)
  • "pink"という文字列があったら"floyd"に変換
  • "EOF"という文字列があったらそこで処理を中断して終了
やりかた

ただやるだけではあるけど、それぞれの処理を初期の文字列に対して並行して行う必要がある。文字列を逐次的に処理すると変換後の文字列を再度変換してしまうので注意。例えば"ddink"は"pink"に変換されるが、逐次的に変換してしまうと"ddink" -> "pink" -> "floyd" になってしまう。"eiieii"なども注意。"iieiieは間違いで"正しくは"ieiiei"になる。
あとEOFは文字列の途中で出てくることがあり、その場合はそこまでを変換して出力する。

以下ソース。

void strReplace(string &str, const string &from, const string &to){
  string::size_type pos = 0;
  while((pos = str.find(from, pos)) != string::npos){
    str.replace(pos, from.length(), to);
    pos += to.length();
  }
}

int main(int argc, char **argv){
  string s;
  while(getline(cin, s)){
    int idx = s.find("EOF");
    bool final = false;
    if(idx != string::npos){
      s = s.substr(0, idx);
      final = true;
    }

    strReplace(s, "dd", "dddpddd");
    strReplace(s, "pink", "floyd");
    strReplace(s, "dddpddd", "p");

    for(int i = 1; i < s.size(); i++){
      if(s[i - 1] == 'e' && s[i] == 'i'){
	if(i == 1 || (2 <= i && s[i - 2] != 'c')){
	  s[i - 1] = 'i', s[i] = 'e'; i++;
	}
      }
    }

    for(unsigned char c = 0; c < 0xff; c++){
      if(('a' <= c && c <= 'z') || c == ' ') continue;
      string d; d += c;
      strReplace(s, d, "");
    }
    cout << s << endl;
    if(final) break;
  }
  return 0;
}

今見ると変数名にfinalとかつけてしまった。もしPOJがc++11だったらCompile Errorになってた。

しょげないでよBaby 眠れば治る