Problem:
百人一首
百人一首は日本の伝統的なゲームである． このゲームでは
N
枚のカードが用いられ，それぞれのカードには1つの文字列が書かれている．細かいルールは省略するが， A君は
N
枚のカードに書かれている文字列を何らかの順序でそれぞれちょうど1回ずつ読むことになった．
百人一首ではその接頭辞が非常に重要である．百人一首で使用される文字列として接頭辞は似ている言葉が多く， 連続して似たような文字列を読むとA君も混乱してしまう． そこでA君は，連続する二つのカードの文字列の接頭辞ができるだけ異なるように，カードを並べ替えようと考えた．
A君の目標は，「山札の読みやすさ」と呼ばれる指標を最小化するような山札を求めることである．ここで山札とは，この百人一首で読まれる
N
枚のカードを並べ替えたものを指す．山札の読みやすさとは，山札内にある隣接したカードの類似度の総和を言う．2 枚のカードの類似度はそれらに書かれた文字列どうしの最長共通接頭辞の長さとして定義される．なお，文字列
t
と
u
の最長共通接頭辞とは，
t
と
u
両方の接頭辞であるような文字列のうち，最長のものを指す．
例えば，2 枚のカードに書かれた文字列がそれぞれ "
jag
" と "
japan
" であれば，これらのカードの類似度は 2 である．一方，"
wan
" と "
nyan
" であれば類似度は 0 である．
ところで，「山札の読みやすさ」が最小となる山札は複数存在するかもしれない．A君は，最小解としてありうる山札のうち，辞書順最小の山札を求めたい．ここで，山札
P
と
Q
について
P
が辞書順で小さいとは，ある正の整数
i
が存在して，1 番目から
i-1
番目のカードまではそれぞれ同一のカードであり，かつ
i
番目のカードの文字列は辞書順において山札
P
のカードのほうが小さいことを言う．
あなたの仕事は，「山札の読みやすさ」を最小にするような山札の中で，辞書順最小となるものを求めることである．
Input
入力は複数のデータセットからなる． 各データセットは次の形式で表される．
N
s
1
...
s
N
1行目にはカードの枚数を表す整数
N
(
1 ≤ N ≤ 100,000
) が与えられる．
続く
N
行にはカードに書かれた1文字以上の文字列が書かれてあり， 1行は英小文字のみで構成される．また，同一セット内において，各
s
i
は互いに異なることが保証されている．
さらに，
s
i
の長さの合計は 400,000 以下であることが保証されている．
入力の終わりは， 1つのゼロからなる行で示す．
Output
各データセットについて，「山札の読みやすさ」が最小となる山札の中で辞書順最小の山札の情報を，
N
行に出力せよ．具体的には，そのような山札の
i
番目のカードに書かれた文字列を，
i
行目に出力することになる．
Sample Input
3
icpc
icfp
topcoder
4
apple
application
appointment
acmicpc
3
a
aa
aaa
4
a
aza
azb
b
0
Output for Sample Input
icfp
topcoder
icpc
apple
acmicpc
application
appointment
aa
a
aaa
a
aza
b
azb
辞書順の定義に注意すること．今回の定義では，文字列を連結した時に辞書順最小になるということを意味するわけではない．例えば，3番目の入力例では [aaa,a,aa] も「山札の読みやすさ」が最小となる解の一つであり，連結すれば同じ文字列になるため，辞書順最小の解に見えるかもしれない．しかしながら，今回の定義では先頭の文字列 "aa" と "aaa" が優先的に比較されるため，辞書順最小の解は [aa,a,aaa] となる．

Submitted solutions:

code_snippet: #include<bits/stdc++.h>
using namespace std;
int main(){
    int n;
    while(cin >> n && n!=0){
        vector<string> s(n);
        for(int i=0;i<n;i++)cin >> s[i];
        sort(s.begin(),s.end());
        vector<int> lcp(n-1);
        for(int i=0;i<n-1;i++){
            for(int j=0;j<min(s[i].size(),s[i+1].size());j++){
                if(s[i][j]==s[i+1][j])lcp[i] = j + 1;
                else break;
            }
        }
        int mx = 0;
        for(int i=0;i<n-1;i++)mx = max(mx,lcp[i]);
        int ok = 0, ng = mx+1;
        while(ng-ok>1){
            int mid = (ok+ng)/2;
            int len = 1, left = 0;
            for(int i=0;i<n-1;i++){
                if(lcp[i] < mid) left = i+1;
                else len = max(len,i+2 - left);
            }
            if(len > n/2) ok = mid;
            else ng = mid;
        }
        int L = 0,R = n;
        for(int i=0;i<n-1;i++){
            if(lcp[i] < ok){
                if((i+1-L)>n/2){
                    R = i+1;
                    break;
                }
                L = i+1;
            }
        }
        vector<queue<int> > p(29);
        for(int i=0;i<n;i++){
            if(i<L)p[0].push(i);
            else if(i>=R)p[28].push(i);
            else if(s[i].size()==ok)p[1].push(i);
            else p[s[i][ok]-'a' + 2].push(i);
        }
        vector<int> c(29);
        for(int i=0;i<29;i++)c[i] = p[i].size();
        vector<int> res(n);
        vector<int> type(n);
        bool strict = false;
        for(int i=0;i<n;i++){
            if(i!=0&&type[i-1]!=0&&type[i-1]!=28&&(n-i)%2==0&&accumulate(c.begin()+1,c.begin()+28,0) == (n-i)/2) strict = true;
            for(int j=0;j<29;j++){
                if(c[j]==0)continue;
                if(i==0&&j==0)continue;
                if(i!=0&& type[i-1]==j)continue;
                if(i!=0&& (type[i-1]==0||type[i-1]==28) && j==0)continue;
                if(strict){
                    if((n-i)%2==0 && (0<j && j<28))continue;
                    if((n-i)%2==1 && (j==0))continue;
                }
                c[j]--;
                bool flag = true;
                for(int k=1;k<28;k++)if(c[k] > (n-i)/2) flag = false;                
                if(flag){
                    res[i] = p[j].front();
                    type[i] = j;
                    p[j].pop();
                    break;
                }
                c[j]++;
            }
        }
        for(int i=0;i<n;i++)cout << s[res[i]] << "\n";
    }
    return 0;
}
accuracy: 1
time_ms: 30
memory_kb: 5216
final_rank: 1

code_snippet: #include<bits/stdc++.h>
using namespace std;
#define rep(i,n) for(int i=0;i<(int)(n);i++)

class Trie {
public:
	struct Node {
		Node *next[26];
		int sub;
        bool flag;
        int id;
        int priority;
		Node() : sub(0),flag(false),id(-1),priority(0){
			for(int i = 0; i < 26; i++){
				next[i] = nullptr;
			}
		}
	};
	Node* root;
	Trie(){
		root = new Node();
	}
	//Trie木にxを加える
	void add(string& s,int id) {
		Node *curr = root;
		for(int i = 0; i < (int)s.size(); i++){
			int y = s[i] - 'a';
			if (!curr->next[y]) {
				curr->next[y] = new Node();
			}
			curr->sub++;
			curr = curr->next[y];
		}
		curr->sub++;
        curr->flag = 1;
        curr->id = id;
	}
    void  init(Node *curr,int &p){
        if(curr->flag){
            curr->priority = p;
            p--;
        }
        for(int i = 0; i < 26; i++){
			if(curr->next[i]){
                init(curr->next[i],p);
            }
		}
    }
    //何らかのクエリ
    void  dfs(Node *curr,priority_queue<pair<int,int > >&res){
        if(curr->flag){
            res.push({curr->priority,curr->id});
        }
        for(int i = 0; i < 26; i++){
			if(curr->next[i]){
                dfs(curr->next[i],res);
            }
		}
    }
	vector<int> query(Node *curr,priority_queue<pair<int,int> > &s) {
        int tt = 0;
        if(curr->flag){
            tt++;
        }
        vector<int> p(26);
        for(int i = 0; i < 26; i++){
			if(curr->next[i]){
                p[i] = curr->next[i]->sub;
            }
		}
        int sm = 0;
        for(int i=0;i<26;i++){
            sm += p[i];
        }
        int nxt = -1;
        for(int i=0;i<26;i++){
            if(p[i] > ((int)s.size()+sm-p[i]+tt)){
                nxt = i;
            }
        }
        if(nxt==-1){
            vector<priority_queue<pair<int,int> > > pq(28);
            for(int i = 0; i < 26; i++){
                if(curr->next[i]&&i!=nxt){
                    dfs(curr->next[i],pq[i]);
                }
            }
            pq[26] = s;
            if(tt==1){
                pq[27].push(make_pair(curr->priority,curr->id));
            }
            vector<int> res;
            int now = 26;
            while(1){
                bool flag = 1;
                for(int i=0;i<28;i++){
                    if(pq[i].size()!=0)flag = 0;
                }
                if(flag)break;
                priority_queue<pair<int,int> > ok;
                for(int i=0;i<28;i++){
                    if(i==now)continue;
                    if(pq[i].size()==0)continue;
                    int mx = 0;
                    int ss = 0;
                    for(int j=0;j<28;j++){
                        ss += pq[j].size();
                        if(j==i)continue;
                        mx = max(mx,(int)pq[j].size());
                        
                    }
                    if(mx > ss - 1 - mx+1 || (i!=26&&pq[26].size() > ss - 1 - pq[26].size())){

                    }else{
                        ok.push(make_pair(pq[i].top().first,i));
                    }
                }
                auto x = ok.top();
                int iii = x.second;
                now = iii;
                res.push_back(pq[iii].top().second);
                pq[iii].pop();
            }
            return res;
        }else{
            if(curr->flag){
                s.push(make_pair(curr->priority,curr->id));
            }
            for(int i = 0; i < 26; i++){
                if(curr->next[i]&&i!=nxt){
                    dfs(curr->next[i],s);
                }
            }
            return query(curr->next[nxt],s);
        }
	}
	vector<int> query() {
        int p = 10000000;
        init(root,p);
        priority_queue<pair<int,int> > pq;
        return query(root,pq);
	}
};

int main(){
    int n;
    while(cin>> n &&n!=0){
        Trie t;
        string s;
        vector<string>S;
        rep(i,n){
            cin >> s;
            S.push_back(s);
            t.add(s,i);
        }
        if(n==2){
            if(S[0]<S[1]){
                cout << S[0] << "\n";
                cout << S[1] << "\n";
            }else{

                cout << S[1] << "\n";
                cout << S[0] << "\n";
            }
            continue;
        }
        vector<int> res = t.query();
        rep(i,n){
            cout << S[res[i]] << "\n";
        }
    }
    return 0;
}
accuracy: 1
time_ms: 210
memory_kb: 99076
final_rank: 4

code_snippet: #include<bits/stdc++.h>
typedef long long int ll;
typedef unsigned long long int ull;
#define BIG_NUM 2000000000
#define HUGE_NUM 1000000000000000000
#define MOD 1000000007
#define EPS 0.000000001
using namespace std;



int N;
int num_T[27],work_T[27];
int rest_T[27];
int num_V[26],work_V[26],rest_V[26];
int index_T[27],index_V[26];
vector<string> T[27];
vector<string> OTHER;
vector<string> V[26];
vector<string> input;

int calc_common_len(string A,string B){

	int ret = 0;
	int tmp_len = min(A.length(),B.length());

	for(int i = 0; i < tmp_len; i++){
		if(A[i] != B[i])break;
		ret++;
	}
	return ret;
}

//過半数文字列なし
void func_not(){

	for(int i = 0; i < 26; i++){

		num_V[i] = 0;
		index_V[i] = 0;
	}

	for(int i = 0; i < N; i++){

		num_V[input[i][0]-'a']++;
		V[input[i][0]-'a'].push_back(input[i]);
	}

	int pre = -1;
	int maximum,num_rest = N;

	for(int i = 0; i < 26; i++){

		rest_V[i] = num_V[i];
	}

	//多分コスト0でクリア可(まずは遇番目に昇順に並べ(0が先頭)、次に奇番目に昇順に並べる)
	for(int loop = 1; loop <= N; loop++){
		for(int i = 0; i < 26; i++){
			if(i == pre || index_V[i] == num_V[i])continue; //同グループ連続はコストが発生するので不可

			if(num_rest == 1){ //★★注意★★
				printf("%s\n",V[i][index_V[i]].c_str());
				return;
			}

			for(int k = 0; k < 26; k++){

				work_V[k] = rest_V[k];
			}

			work_V[i]--;
			int work_num_rest = num_rest-1;


			/*
			 * グループiの残数が1減った結果、過半数に達するグループが現れて、
			 * そのグループが隣接せざるを得なくなったら不可
			 * */
			maximum = -1;

			for(int k = 0; k < 26; k++){

				maximum = max(maximum,work_V[k]);
			}

			if(work_num_rest%2 == 0){

				if(maximum > work_num_rest/2){ //隣接せざるを得なくなるので不可

					continue;
				}

			}else{  //隣接せざるを得なくなるので不可

				if(maximum > (work_num_rest+1)/2){

					continue;
				}
			}

			printf("%s\n",V[i][index_V[i]].c_str());
			index_V[i]++;
			rest_V[i]--;
			pre = i;

			break;
		}
		num_rest--;
	}
}

void func(){

	for(int i = 0; i < 27; i++){

		T[i].clear();
	}
	OTHER.clear();
	for(int i = 0; i < 26; i++){

		V[i].clear();
	}
	input.clear();


	for(int i = 0; i < N; i++){

		string tmp_str;
		cin >> tmp_str;

		input.push_back(tmp_str);
	}

	sort(input.begin(),input.end());

	int max_len = -1,start_index;
	string COMMON;

	int add = N/2;

	//過半数文字があるか調べる
	for(int i = 0; i+add <= N-1; i++){

		int tmp = calc_common_len(input[i],input[i+add]);
		if(tmp == 0)continue;

		if(max_len < tmp){
			max_len = tmp;
			start_index = i;
			COMMON = input[i].substr(0,max_len);
		}
	}

	if(max_len == -1){

		func_not();
		return;
	}

	//COMMONを接頭辞として持つ範囲を求める
	int tail = start_index+add;
	while(true){

		if(tail+1 == N)break;

		int tmp = calc_common_len(COMMON,input[tail+1]);

		if(tmp == max_len){

			tail++;

		}else{

			break;
		}
	}

	int num_OTHER = 0;
	int index_OTHER = 0;

	for(int i = 0; i < 27; i++){ //0:空白文字  (1～26):'a'を1とし、'z':26まで

		num_T[i] = 0;
		index_T[i] = 0;
	}

	for(int i = 0; i < start_index; i++){

		OTHER.push_back(input[i]);
		num_OTHER++;
	}
	for(int i = tail+1; i < N; i++){

		OTHER.push_back(input[i]);
		num_OTHER++;
	}

	//COMMONのグループは、さらに子グループを調べる
	for(int i = start_index; i <= tail; i++){

		if(input[i].length() == COMMON.length()){

			num_T[0]++;
			T[0].push_back(COMMON);

		}else{

			int ch = (input[i][COMMON.length()]-'a')+1;
			num_T[ch]++;
			T[ch].push_back(input[i]);
		}
	}

	int adj_rest = 0; //COMMONグループを隣接せざるを得ない回数
	int num_COMMON = N-num_OTHER;

	if(N%2 == 0){

		if(num_COMMON-N/2 > 0){

			adj_rest = 2*(num_COMMON-N/2)-1;
		}

	}else{

		if(num_COMMON-(N+1)/2 > 0){

			adj_rest = 2*(num_COMMON-(N+1)/2);
		}
	}

	if(adj_rest == 0){ //ぴったり交互に配置できる場合[★Nは奇数であるはず★]

		//COMMON,OTHERともに昇順に出力すれば良い
		for(int loop = 1; loop <= N; loop++){
			if(loop%2 == 1){

				for(int i = 0; i < 27; i++){
					if(index_T[i] == num_T[i])continue;

					printf("%s\n",T[i][index_T[i]].c_str());
					index_T[i]++;
					break;
				}
			}else{

				printf("%s\n",OTHER[index_OTHER].c_str());
				index_OTHER++;
			}
		}
		return;
	}

	int pre = -2; //OTHERなら-1,COMMONなら0～26
	string min_str;

	int num_rest = N;

	int rest_OTHER = num_OTHER;
	for(int i = 0; i < 27; i++){

		rest_T[i] = num_T[i];
	}

	bool out_FLG;

	for(int loop = 1; loop <= N; loop++){

		min_str = "}";
		out_FLG = false;

		if(pre != -1 && index_OTHER < num_OTHER){ //その他の中で辞書順最小のもの

			if(num_rest == 1){

				printf("%s\n",OTHER[index_OTHER].c_str());
				return;
			}

			int work_num_rest = num_rest-1;

			int calc_must_adj; //COMMONが隣り合わなければいけない回数

			/*
			 * 過半数文字の文字長が伸びる可能性あり、また隣接せざるを得ない回数が増える可能性あり
			 * */

			if(work_num_rest%2 == 0){

				calc_must_adj = 2*(num_COMMON-(work_num_rest)/2)-1;

			}else{

				calc_must_adj = 2*(num_COMMON-(work_num_rest+1)/2);
			}

			if(calc_must_adj == adj_rest){ //隣接せざるを得ない回数が増えない場合

				int maximum = -1,max_index;
				for(int k = 0; k < 27; k++){

					if(maximum < rest_T[k]){
						maximum = rest_T[k];
						max_index = k;
					}
				}

				if(max_index == 0 || (work_num_rest%2 == 0 && maximum <= work_num_rest/2) ||
						(work_num_rest%2 == 1 && maximum <= (work_num_rest+1)/2)){ //過半数文字が伸びない、または伸びても隣接しなければOK

					min_str = OTHER[index_OTHER];
				}
			}
		}

		if(adj_rest == 0 && pre >= 0){ //もう隣接しない状態で、前回がCOMMONグループ

			//OTHERを出力
			pre = -1;
			printf("%s\n",OTHER[index_OTHER].c_str());
			index_OTHER++;
			rest_OTHER--;
			num_rest--;

			continue;
		}

		if(num_COMMON > 0){

			for(int i = 0; i < 27; i++){ //辞書順でみる
				if(index_T[i] == num_T[i] || i == pre)continue;

				if(num_rest == 1){ //★★注意★★

					printf("%s\n",T[i][index_T[i]].c_str());
					return;
				}

				for(int k = 0; k < 27; k++){

					work_T[k] = rest_T[k];
				}

				work_T[i]--;

				int work_num_rest = num_rest-1;
				int maximum = -1,max_index;

				for(int k = 0; k < 27; k++){

					if(maximum < work_T[k]){
						maximum = work_T[k];
						max_index = k;
					}
				}

				bool FLG = true;

				if(max_index > 0){ //★過半数文字長が伸び、かつ隣接せざるを得ないなら不可★

					if(work_num_rest%2 == 0 && maximum > work_num_rest/2){

						FLG = false; //コスト増なので不可

					}else if(work_num_rest%2 == 1 && maximum > (work_num_rest+1)/2){

						FLG = false; //コスト増なので不可

					}
				}

				if(FLG){

					if(T[i][index_T[i]] < min_str){

						if(pre >= 0){

							adj_rest--; //隣接せざるを得ない回数
						}

						pre = i;
						printf("%s\n",T[i][index_T[i]].c_str());
						out_FLG = true;
						index_T[i]++;
						rest_T[i]--;
						num_COMMON--;
						num_rest--;
						break;
					}
				}
			}
		}

		if(out_FLG)continue;

		//OTHERを出力
		pre = -1;
		printf("%s\n",OTHER[index_OTHER].c_str());
		index_OTHER++;
		rest_OTHER--;
		num_rest--;
	}
}

int main(){

	while(true){
		scanf("%d",&N);
		if(N == 0)break;

		func();
	}

	return 0;
}
accuracy: 1
time_ms: 90
memory_kb: 6432
final_rank: 2

code_snippet: #include<bits/stdc++.h>
typedef long long int ll;
typedef unsigned long long int ull;
#define BIG_NUM 2000000000
#define HUGE_NUM 1000000000000000000
#define MOD 1000000007
#define EPS 0.000000001
using namespace std;



int N;
int num_T[27],work_T[27];
int rest_T[27];
int num_V[26],work_V[26],rest_V[26];
int index_T[27],index_V[26];
vector<string> T[27];
vector<string> OTHER;
vector<string> V[26];
vector<string> input;

int calc_common_len(string A,string B){

	int ret = 0;
	int tmp_len = min(A.length(),B.length());

	for(int i = 0; i < tmp_len; i++){
		if(A[i] != B[i])break;
		ret++;
	}
	return ret;
}

//過半数文字列なし
void func_not(){

	for(int i = 0; i < 26; i++){

		num_V[i] = 0;
		index_V[i] = 0;
	}

	for(int i = 0; i < N; i++){

		num_V[input[i][0]-'a']++;
		V[input[i][0]-'a'].push_back(input[i]);
	}

	int pre = -1;
	int maximum,num_rest = N;

	for(int i = 0; i < 26; i++){

		rest_V[i] = num_V[i];
	}

	//多分コスト0でクリア可
	for(int loop = 1; loop <= N; loop++){
		for(int i = 0; i < 26; i++){
			if(i == pre || index_V[i] == num_V[i])continue; //同グループ連続はコストが発生するので不可

			if(num_rest == 1){
				printf("%s\n",V[i][index_V[i]].c_str());
				return;
			}

			for(int k = 0; k < 26; k++){

				work_V[k] = rest_V[k];
			}

			work_V[i]--;
			int work_num_rest = num_rest-1;


			/*
			 * グループiの残数が1減った結果、過半数に達するグループが現れて、
			 * そのグループが隣接せざるを得なくなったら不可
			 * */
			maximum = -1;
			int max_id;

			for(int k = 0; k < 26; k++){

				if(maximum < work_V[k]){
					maximum = work_V[k];
					max_id = k;
				}
			}

			if(work_num_rest%2 == 0){

				if(maximum > work_num_rest/2){ //隣接せざるを得なくなるので不可

					continue;
				}

			}else{  //隣接せざるを得なくなるので不可

				if(maximum > (work_num_rest+1)/2){

					continue;
				}
			}

			printf("%s\n",V[i][index_V[i]].c_str());
			index_V[i]++;
			rest_V[i]--;
			pre = i;

			break;
		}
		num_rest--;
	}
}

void func(){

	for(int i = 0; i < 27; i++){

		T[i].clear();
	}
	OTHER.clear();
	for(int i = 0; i < 26; i++){

		V[i].clear();
	}
	input.clear();


	for(int i = 0; i < N; i++){

		string tmp_str;
		cin >> tmp_str;

		input.push_back(tmp_str);
	}

	sort(input.begin(),input.end());

	int max_len = -1,start_index;
	string COMMON;

	int add = N/2;

	for(int i = 0; i+add <= N-1; i++){

		int tmp = calc_common_len(input[i],input[i+add]);
		if(tmp == 0)continue;

		if(max_len < tmp){
			max_len = tmp;
			start_index = i;
			COMMON = input[i].substr(0,max_len);
		}
	}

	if(max_len == -1){

		func_not();
		return;
	}

	//COMMONを接頭辞として持つ範囲を求める
	int tail = start_index+add;
	while(true){

		if(tail+1 == N)break;

		int tmp = calc_common_len(COMMON,input[tail+1]);

		if(tmp == max_len){

			tail++;

		}else{

			break;
		}
	}

	int num_OTHER = 0;
	int index_OTHER = 0;

	for(int i = 0; i < 27; i++){ //0:空白文字  (1～26):'a'を1とし、'z':26まで

		num_T[i] = 0;
		index_T[i] = 0;
	}

	for(int i = 0; i < start_index; i++){

		OTHER.push_back(input[i]);
		num_OTHER++;
	}
	for(int i = tail+1; i < N; i++){

		OTHER.push_back(input[i]);
		num_OTHER++;
	}

	//COMMONのグループは、さらに子グループを調べる
	for(int i = start_index; i <= tail; i++){

		if(input[i].length() == COMMON.length()){

			num_T[0]++;
			T[0].push_back(COMMON);

		}else{

			int ch = (input[i][COMMON.length()]-'a')+1;
			num_T[ch]++;
			T[ch].push_back(input[i]);
		}
	}

	int adj_rest = 0; //COMMONグループを隣接せざるを得ない回数
	int num_COMMON = N-num_OTHER;

	if(N%2 == 0){

		if(num_COMMON-N/2 > 0){

			adj_rest = 2*(num_COMMON-N/2)-1;

		}

	}else{

		if(num_COMMON-(N+1)/2 > 0){

			adj_rest = 2*(num_COMMON-(N+1)/2);
		}
	}

	if(adj_rest == 0){ //ぴったり交互に配置できる場合[★Nは奇数であるはず★]

		//COMMON,OTHERともに昇順に出力すれば良い

		for(int loop = 1; loop <= N; loop++){
			if(loop%2 == 1){

				for(int i = 0; i < 27; i++){
					if(index_T[i] == num_T[i])continue;

					printf("%s\n",T[i][index_T[i]].c_str());
					index_T[i]++;
					break;
				}
			}else{

				printf("%s\n",OTHER[index_OTHER].c_str());
				index_OTHER++;
			}
		}
		return;
	}

	/*printf("COMMON:%s adj_rest:%d\n",COMMON.c_str(),adj_rest);
	printf("num_OTHER:%d\n",num_OTHER);
	for(int i = 0; i < 27; i++){

		printf("T[%d]:%d\n",i,num_T[i]);
	}*/

	//return;

	int pre = -2; //OTHERなら-1,COMMONなら0～26
	string min_str;

	int num_rest = N;

	int rest_OTHER = num_OTHER;
	for(int i = 0; i < 27; i++){

		rest_T[i] = num_T[i];
	}

	bool out_FLG;

	/*for(int i = 0; i < num_OTHER; i++){

		printf("OTHER %d:%s\n",i,OTHER[i].c_str());
	}

	printf("COMMON:%s\n",COMMON.c_str());

	printf("adj_rest:%d num_OTHER:%d\n",adj_rest,num_OTHER);
	printf("\n\n");*/

	for(int loop = 1; loop <= N; loop++){

		//printf("\n");

		min_str = "}";
		out_FLG = false;

		if(pre != -1 && index_OTHER < num_OTHER){ //その他の中で辞書順最小のもの

			if(num_rest == 1){

				printf("%s\n",OTHER[index_OTHER].c_str());
				return;
			}

			int work_num_rest = num_rest-1;

			int calc_must_adj; //COMMONが隣り合わなければいけない回数

			/*
			 * COMMONの文字数が伸びる可能性あり、また隣接せざるを得ない回数が増える可能性あり
			 * */

			if(work_num_rest%2 == 0){

				calc_must_adj = 2*(num_COMMON-(work_num_rest)/2)-1;

			}else{

				calc_must_adj = 2*(num_COMMON-(work_num_rest+1)/2);
			}

			if(calc_must_adj == adj_rest){ //隣接せざるを得ない回数が増えない場合

				int maximum = -1,max_index;
				for(int k = 0; k < 27; k++){

					if(maximum < rest_T[k]){
						maximum = rest_T[k];
						max_index = k;
					}
				}

				//printf("maximum:%d max_index:%d work_num_rest:%d\n",maximum,max_index,work_num_rest);

				if(max_index == 0 || (work_num_rest%2 == 0 && maximum <= work_num_rest/2) ||
						(work_num_rest%2 == 1 && maximum <= (work_num_rest+1)/2)){ //過半数文字が伸びない、または伸びても隣接しなければOK

					//printf("過半数文字が伸びない\n");
					min_str = OTHER[index_OTHER];
				}else{

					//printf("過半数文字が伸びる\n");
				}
			}else{

				//printf("adj_restが増える\n");
			}
		}

		if(adj_rest == 0 && pre >= 0){ //もう隣接しない状態で、前回がCOMMONグループ

			//printf("もう隣接しない\n");

			//OTHERを出力
			pre = -1;
			printf("%s\n",OTHER[index_OTHER].c_str());
			index_OTHER++;
			rest_OTHER--;
			num_rest--;

			continue;
		}

		if(num_COMMON > 0){

			for(int i = 0; i < 27; i++){ //辞書順でみる
				if(index_T[i] == num_T[i] || i == pre)continue;

				if(num_rest == 1){

					printf("%s\n",T[i][index_T[i]].c_str());
					return;
				}

				for(int k = 0; k < 27; k++){

					work_T[k] = rest_T[k];
				}

				work_T[i]--;

				int work_num_rest = num_rest-1;
				int maximum = -1,max_index;

				for(int k = 0; k < 27; k++){

					if(maximum < work_T[k]){
						maximum = work_T[k];
						max_index = k;
					}
				}

				bool FLG = true;

				//printf("i:%d maximum:%d num_rest:%d\n",i,maximum,num_rest);

				if(max_index > 0){ //COMMONの文字が伸びた

					if(work_num_rest%2 == 0 && maximum > work_num_rest/2){
						//printf("のびた\n");
						FLG = false; //コスト増なので不可

					}else if(work_num_rest%2 == 1 && maximum > (work_num_rest+1)/2){
						//printf("のびた\n");
						FLG = false; //コスト増なので不可

					}
				}

				if(FLG){

					if(T[i][index_T[i]] < min_str){

						//printf("COMMON出力\n");

						if(pre >= 0){

							adj_rest--; //隣接せざるを得ない回数
						}

						pre = i;
						printf("%s\n",T[i][index_T[i]].c_str());
						out_FLG = true;
						index_T[i]++;
						rest_T[i]--;
						num_COMMON--;
						num_rest--;
						break;
					}
				}
			}
		}

		if(out_FLG)continue;

		//OTHERを出力
		pre = -1;
		//printf("OHTER出力\n");
		printf("%s\n",OTHER[index_OTHER].c_str());
		index_OTHER++;
		rest_OTHER--;
		num_rest--;
	}
}

int main(){

	while(true){
		scanf("%d",&N);
		if(N == 0)break;

		func();
	}

	return 0;
}
accuracy: 1
time_ms: 90
memory_kb: 6572
final_rank: 3