Problem:
Princess' Japanese

お姫様の日本語

English text is not available in this practice contest.

ある貧乏な国のおてんばで勇敢なお姫様は，日本語の勉強をしている．負けず嫌いな性格も手伝って，お姫様は日本語を流暢に話せるようになったが，発音に少しばかり不自然なところが残っている．どうやら，母音の無声化という日本語固有の発音法が関係しているらしい．お姫様は完璧な日本語話者を目指しているため，何としても自分の不自然な発音を矯正したいと思っている．そこでお姫様は，従者であるあなたに対して，発音を矯正するためのプログラムを作成するように命じた．あなたの仕事は，入力で与えられた日本語の語句に対して，無声化の生じる様子を示すプログラムを作成することである．

お姫様はひらがな，カタカナ，日常的に使用される漢字など，日本語の表記体系について十分に習得しているため，読み書きに困ることはめったにない．ところが，お姫様の国にあるコンピュータでは日本語の入力に困難があるため，プログラムにおいては，「ローマ字」と呼ばれる英文字による表記を用いることに決めた．

まず，日本語の発音体系について大まかに説明する必要があるだろう．日本語では拍（モーラ）を単位として発音がなされる．拍は以下のいずれかの形をしている．

（母音）

（子音）+（母音）

（「y」「w」以外の子音）+「y」+（母音）

促音（「っ」）

撥音（「ん」）

長音（「ー」）

ここで，母音は「a」「i」「u」「e」「o」のいずれか，子音は「k」「s」「t」「n」「h」「m」「y」「r」「w」「g」「z」「d」「b」「p」のいずれかである．ただし，「y」の直後に現れる母音は「a」「u」「o」に限られる．また，「w」の直後に現れる母音は「a」に限られる．

促音は直後の拍の子音を重ねて表記される．たとえば，「sekki」（石器）は促音を含む語句のひとつである．促音は母音あるいは「n」「y」「w」の前には現れない．まれに促音が文節末に現れることはあるが，ここではそのような例は考えない．

撥音は単独の「n」によって表記される．ただし，母音または「y」の直前に現れるときは，上記の2.または3.との区別がつかなくなるため，「n」の直後にアポストロフィーをつける．以下にいくつかの例をしめす．

tango（単語），ningen（人間），han'ei（繁栄）

特に，以下の違いには注意されたい．

zenin（是認），zen'in（全員），zennin（前任）

長音は単独で音節を構成するわけではないが，常に1拍を余分に使って（すなわち2拍で）発音される．日本語では長音と短音の対立があり，しかも長音と短音の違いによって語句が区別されるため，長音と短音の違いは重要である．ここでは長音は母音（「a」「i」「u」のいずれか）を重ねて表記することとする．以下にいくつかの例をしめす．

raamen（ラーメン），biiru（ビール），kyuusyuu（九州），karei（カレー），kyouto（京都）

撥音，促音，長音は先頭に現れず，また連続して現れない．また撥音と促音が互いに隣り合って現れることもない．

さて，日本語では特定の条件下において母音がきちんと発音されないことがある．これが母音の無声化と呼ばれる現象である．母音が無声化される条件に関しては諸説あるが，ここでは比較的簡単でかつ機械的適用が可能である以下のような場合について考えることにする．ただし，無声子音とは「k」「s」「t」「h」「p」のことである．また，これらに「y」が続いたもの（「ky」など）も便宜的に無声子音とみなす．

母音「i」「u」の短音について，その母音が無声子音にはさまれたとき，または無声子音の直後でしかも語句の末尾にあるとき．この規則は，母音「i」「u」の直後の拍が促音であっても適用される．以下にいくつかの例を示す．

kusa（草），hati（蜂），kiku（菊），kitte（切手），kippu（切符），suupu（スープ）

母音「a」「o」について，同一の母音が無声子音とともに連続する2つ以上の拍で現れたとき．ただし，その連続する拍のうちで最後のものについては除外される．以下にいくつかの例を示す．促音および長音はひとつの拍を構成することから，連続部分の先頭または途中に，促音または長音があってはならないという点には注意すること．

haha（母），koto（琴），hakata（博多），sakana（魚），kakato（かかと）

ただし，上記に該当する全部の母音が無声化されるわけではない．これは，複数の母音が続けて無声化されることはないことによる．母音が無声化されるのは，最初の母音である場合，または直近の母音が無声化されなかった場合であり，またそれらの場合にかぎる．たとえば，「hakata」については，最初の「a」は無声化されるが，2番目の「a」は無声化されない．同様にして，「kippu」については，語中の「i」は無声化されるが，語尾の「u」は無声化されずに発音される．

「hottokouhii」（ホットコーヒー）はわかりにくい例のひとつである．これを拍に分解すると「ho-（促音）-to-ko-（長音）-hi-（長音）」のようになる．最初の「o」は，直後の拍が促音であるため，上記の注意書きにあるように「無声子音 + o」が連続しているとはみなされない．すなわち，最初（1拍目）の「o」は無声化されない．ところが，次の「o」については，直後に長音があるものの「to」「ko」というように「無声子音 + o」が連続して現れている．したがって，2番目（3拍目）の「o」は無声化される．また，語尾付近に無声子音「h」の後に現れる「i」が存在するが，これは長音であるため無声化されない．

Input

入力は複数のデータセットからなる．それぞれのデータセットは100文字以下の1つの語句が含まれる1行だけからなる．それぞれの語句には英小文字とアポストロフィーだけが含まれる．また，それぞれの語句は，問題文中で説明した拍だけから構成されるが，標準日本語の語句であるとは限らない．

入力の終わりは「#」のみが含まれる行によって表される．これはデータセットに含まれない．

Output

それぞれのデータセットについて，与えられた語句を，無声化される母音を括弧で囲んだうえで一行に出力しなさい．

Sample Input

kusa
haha
hakata
kippu
sasakisan
sosonokasu
hottokouhii
i
#

Output for the Sample Input

k(u)sa
h(a)ha
h(a)kata
k(i)ppu
s(a)sak(i)san
s(o)sonokas(u)
hott(o)kouhii
i

Solution in JAVA (judge verdict: Accepted)

import java.util.*;
import java.lang.*;
import java.math.*;
import java.io.*;
import static java.util.Arrays.*;
import static java.util.Collections.*;
import static java.lang.Math.*;

public class Main {

	Scanner sc = new Scanner(System.in);

	String s;

	void run() {
		for (;;) {
			s = sc.nextLine();
			if (s.equals("#")) {
				break;
			}
			solve();
		}
	}

	int Boin = 0, ShiBo = 1, Sya = 2, Xtu = 3, Nn = 4, Long = 5;
	String[] memo = { "Boin", "ShiBo", "Sya", "Xtu", "Nn", "Long" };

	void solve() {
		s += "$$$";
		char[] cs = s.toCharArray();

		ArrayList<R> list = new ArrayList<R>();

		for (int at = 0; at < cs.length;) {
			boolean check = false;
			// debug("at", at);
			if (cs[at] == '$') {
				break;
			}
			if (isBoin(cs[at])) {
				check = true;
				// debug(cs[at]);
				list.add(new R(s.substring(at, at + 1), at, Boin));
				at++;
			} else if (isShiin(cs[at]) && isBoin(cs[at + 1])) {
				check = true;
				// debug(cs[at], cs[at + 1]);
				list.add(new R(s.substring(at, at + 2), at, ShiBo));
				at += 2;
			} else if (isShiin(cs[at]) && not(cs[at], "yw")
					&& cs[at + 1] == 'y' && isBoin(cs[at + 2])) {
				// check y,w
				check = true;
				list.add(new R(s.substring(at, at + 3), at, Sya));
				// debug(cs[at], cs[at + 1], cs[at + 2]);
				at += 3;
			} else if (isShiin(cs[at]) && cs[at] == cs[at + 1]
					&& not(cs[at], "nyw")) {
				// debug(cs[at]);
				list.add(new R(s.substring(at, at + 1), at, Xtu));
				at++;
			} else if (cs[at] == 'n' && cs[at + 1] == '\'') {
				// debug(cs[at], cs[at + 1]);
				list.add(new R(s.substring(at, at + 2), at, Nn));
				at += 2;
			} else if (cs[at] == 'n'
					&& (isShiin(cs[at + 1]) || cs[at + 1] == '$')) {
				// debug(cs[at]);
				list.add(new R(s.substring(at, at + 1), at, Nn));
				at += 1;
			}
			if (check) {
				// if (false)
				if (hit(cs[at], "aiu")) {
					// debug(cs[at]);
					// list.add(new R(s.substring(at, at + 1), at, Long));
					R r = list.get(list.size() - 1);
					r.type = Long;
					r.s += cs[at];
					at++;
				}
				if (false)
					if (cs[at - 1] == cs[at]) {
						// debug(cs[at]);
						list.add(new R(s.substring(at, at + 1), at, Long));
						at++;
					}
			}
		}
		// debug();
		for (R r : list) {
//			debug(r.s, memo[r.type], r.shiin, r.boin);
		}
		R[] rs = list.toArray(new R[0]);
		int m = rs.length;
		boolean[] hit = new boolean[m];

		for (int i = 0; i < m; i++) {
			if ((rs[i].type == ShiBo || rs[i].type == Sya)
					&& hit(rs[i].boin, "iu") && isMuseiShiin(rs[i].s.charAt(0))) {
				if (i == m - 1 || isMuseiShiin(rs[i + 1].s.charAt(0))) {
					hit[i] = true;
				}
			}
		}
		// debug(hit);
		for (int i = m - 1; i >= 0; i--) {
			if ((rs[i].type == ShiBo || rs[i].type == Sya)
					&& hit(rs[i].boin, "ao") && isMuseiShiin(rs[i].s.charAt(0))) {
			} else {
				continue;
			}
			int j;
			for (j = i; j < m; j++) {
				if ((rs[j].type == ShiBo || rs[j].type == Sya)
						&& rs[j].boin == rs[i].boin
						&& isMuseiShiin(rs[j].s.charAt(0))) {
					// ok
				} else {
					break;
				}
			}
			if (j < m && rs[j].type == Long && rs[j].boin == rs[i].boin
					&& isMuseiShiin(rs[j].s.charAt(0))) {
				j++;
			}
			// debug(i, j);
			for (int k = i; k < j; k++) {
				hit[k] = true;
			}
			hit[j - 1] = false;
		}
		// debug(hit);
		for (int i = 0; i < m; i++) {
			int at = -1;
			for (int j = i - 1; j >= 0; j--) {
				if (rs[j].boin != '$') {
					at = j;
					break;
				}
			}
			if (at == -1 || !hit[at]) {
			} else {
				hit[i] = false;
			}
		}
		// debug(hit);
		String ans = "";
		for (int i = 0; i < m; i++) {
			String add;
			if (hit[i]) {
				add = rs[i].shiin + "(" + rs[i].boin + ")";
			} else {
				add = rs[i].s;
			}
			ans += add;
		}
		// debug(ans);
		println(ans);
	}

	class R {
		String s, shiin;
		char boin;
		int offset, type;

		R(String s, int offset, int type) {
			this.s = s;
			boin = '$';
			shiin = s;
			for (char c : s.toCharArray()) {
				if (isBoin(c)) {
					boin = c;
					shiin = s.substring(0, s.length() - 1);
				}
			}
			this.offset = offset;
			this.type = type;
		}
	}

	boolean hit(char c, String s) {
		return s.indexOf(c) >= 0;
	}

	boolean not(char c, String s) {
		return s.indexOf(c) == -1;
	}

	boolean isBoin(char c) {
		return "aiueo".indexOf(c) >= 0;
	}

	boolean isShiin(char c) {
		return "kstnhmyrwgzdbp".indexOf(c) >= 0;
	}

	boolean isMuseiShiin(char c) {
		return "ksthp".indexOf(c) >= 0;
	}

	void debug(Object... os) {
		System.err.println(deepToString(os));
	}

	void println(String s) {
		System.out.println(s);
	}

	public static void main(String[] args) {
		new Main().run();
	}

}