#include <iostream>
#include <cstdio>

using namespace std;

const int NMAX = 100;

int len;
char t[NMAX][3];

int n, m;
bool a[NMAX+1][NMAX]; //a[i][j]=true, если идет ребро из i в j слева направо
int lm[NMAX+1]; //lm[i] - номер вершины справа, куда идёт ребро пар-я, или -1, если свободна
int rm[NMAX]; //аналогично
bool mark[NMAX]; //отмечать вершины справа при поиске в глубину

bool match(int k, char s[]) {//соответствует ли число шаблону
  char l = k/10+'0';
  if (l!=s[0] && s[0]!='*') return false;
  char r = k%10+'0';
  if (r!=s[1] && s[1]!='*') return false;
  return true;
}

bool dfs(int u){ //поиск в глубину чередующейся цепочки и изменение паросочетания
   for(int v=0; v<m; v++){
      if (a[u][v] && !mark[v]) {
         mark[v]=true;
         if ( rm[v]==-1 || dfs(rm[v]) ) {
            lm[u]=v;
            rm[v]=u;
            return true;
         }
      }
   }
   return false;
}

int main() {
//  freopen("missing.in","r",stdin);
//  freopen("missing.out","w",stdout);
  cin >> m;  n=m+1;
  for (int i=0; i<m; i++) {
     cin >> t[i];
  }
    
  //формируем граф
  for(int j=0; j<n; j++) { //числа - левая доля графа
     for (int i=0; i<m; i++) { //шаблоны - правая доля графа
        if (match(j,t[i])) a[j][i]=true;
     }
  }
 
  //ищем первое паросочетание алгоритмом Куна
  for(int i=0; i<n; i++) lm[i]=-1;
  for(int i=0; i<m; i++) rm[i]=-1;
  for(int i=n-1; i>=0; i--) { //идем по вершинам слева справа налево
     for(int i=0; i<m; i++) mark[i]=false; //обнуляем mark
     if(lm[i]==-1) dfs(i);         
  }
  
  //выводим первый ответ
  for(int i=0; i<n; i++) 
     if (lm[i]==-1) printf("%02d",i);

  //ищем второе паросочетание алгоритмом Куна
  for(int i=0; i<n; i++) lm[i]=-1;
  for(int i=0; i<m; i++) rm[i]=-1;
  for(int i=0; i<n; i++) { //идем по вершинам слева
     for(int i=0; i<m; i++) mark[i]=false; //обнуляем mark
     if(lm[i]==-1) dfs(i);         
  }

  //выводим второй ответ
  for(int i=0; i<n; i++) 
     if (lm[i]==-1) printf(" %02d\n",i);

  return 0;
}
