#include <algorithm>
#include <cstring>
#include <iostream>
#include <vector>

int main(int argc,char *argv[])
{
  std::ios_base::sync_with_stdio(false) ; // this makes an actual difference!
  char buffer[0x10000] ; // extent if needed or make it dynamic if you like
  std::vector<size_t>      I ; I.reserve(0x100) ; // field indices to sort
  std::vector<char const*> P ; P.reserve(0x100) ; // field pointers
  std::vector<char>        F ; F.reserve(0x100) ; // bools to mark duplicated fields (i don't like vector<bool>)
  while (std::cin.getline(buffer,sizeof(buffer)))
  {
    // setup index and pointer array for all fields
    I.clear() ; P.clear() ;
    size_t n = 0 ;
    I.push_back(n) ; P.push_back(buffer) ; ++n ;
    {
      auto p = buffer ;
      while (*p != '\0')
      {
	if (*p == ';')
	{
	  *p = '\0' ; ++p ;
	  I.push_back(n) ; P.push_back(p) ; ++n ;
	}
	else ++p ;
      }
    }
    { // sort fields
      struct Compare
      {
	Compare(std::vector<char const*> const &P) : P(&P) {}
	bool operator()(size_t lhs,size_t rhs) const
	{
	  return strcmp((*P)[lhs],(*P)[rhs]) < 0 ;
	}
	std::vector<char const*> const *P ;
      } ;
      std::stable_sort(&I[3],&I[I.size()],Compare(P)) ;
    }
    { // mark duplicates
      F.clear() ; F.resize(n) ;
      size_t i=3,j=4 ;
      while (j < n)
      {
	if (0 == strcmp(P[I[i]],P[I[j]]))
	{
	  F[I[j]] = 1 ;
	  ++j ;
	}
	else
	{
	  i = j ; ++j ;
	}
      }
    }
    // print result
    for (size_t i=0 ; i<n-1 ; ++i)
    {
      if (!F[i])
	std::cout << P[i] ;
      std::cout << ';' ;
    }
    if (!F[n-1])
      std::cout << P[n-1] ;
    std::cout << '\n' ;
  }
  if (!std::cin.eof())
  {
    std::cerr << "line probably too long (please extend buffer size)\n" ;
    exit(1) ;
  }
  return 0 ;
}
