#include <stdio.h>
#include <iostream>
#include <string.h>
#include <vector>
#include <map>
#include <set>
using namespace std;
#define MAX_CHAR_IN_LINE (256 + 2)
static char s_buf[MAX_CHAR_IN_LINE];
class Fragments
{
private:
typedef map<int, vector<string*>*>::iterator MapIter;
struct FragmentsPair
{
FragmentsPair(vector<string*>* shorterStrs, vector<string*>* longerStrs,
int shorterLen, int longerLen) :
m_shorterStrs(shorterStrs), m_longerStrs(longerStrs),
m_shorterlen(shorterLen), m_longerLen(longerLen)
{
}
int m_shorterlen;
int m_longerLen;
vector<string*>* m_shorterStrs;
vector<string*>* m_longerStrs;
};
public:
~Fragments()
{
MapIter mapIter = m_fragments.begin();
while (mapIter != m_fragments.end())
{
if (mapIter->second)
{
vector<string*>::iterator vecIter = mapIter->second->begin();
while (vecIter != mapIter->second->end())
{
delete (*vecIter);
++vecIter;
}
delete mapIter->second;
}
++mapIter;
}
}
void AddFragment(char* line, int len)
{
MapIter iter = m_fragments.find(len);
if (iter == m_fragments.end())
{
m_fragments.insert(pair<int, vector<string*>*>(len, new vector<string*>));
iter = m_fragments.find(len);
}
iter->second->push_back(new string(line));
}
void OutputOriginalFile()
{
string originalFile;
Reorganize(originalFile);
cout << originalFile.c_str() << endl;
}
private:
void Preparation(int& targetLen, int& totalCnt, size_t& minSameLenFragmentsCnt)
{
MapIter iter = m_fragments.begin();
int totalLen = 0;
totalCnt = 0;
minSameLenFragmentsCnt = iter->second->size();
while (iter != m_fragments.end())
{
size_t vecSize = iter->second->size();
totalLen += (iter->first) * vecSize;
totalCnt += vecSize;
if (vecSize < minSameLenFragmentsCnt)
minSameLenFragmentsCnt = vecSize;
++iter;
}
targetLen = (totalLen * 2) / totalCnt;
}
void GetFragmentsPairs(int targetLen, int totalCnt, size_t minSameLenFragmentsCnt,
vector<FragmentsPair>& fragmentsPairs, int& startPointIndex)
{
MapIter iter = m_fragments.begin();
bool even = ((targetLen % 2) == 0);
int halfLen = targetLen / 2;
startPointIndex = 0;
while (iter != m_fragments.end())
{
if (iter->first <= halfLen)
{
vector<string*>* longerStrs = NULL;
if ((!even) || (iter->first < halfLen))
longerStrs = m_fragments.find(targetLen - iter->first)->second;
fragmentsPairs.push_back(FragmentsPair(iter->second, longerStrs, iter->first, targetLen - iter->first));
if (iter->second->size() == minSameLenFragmentsCnt)
startPointIndex = fragmentsPairs.size() - 1;
}
++iter;
}
}
bool StrStartsWith(const string& refStr, const string& subStr)
{
int i = 0;
while(subStr[i] != 0)
{
if (refStr[i] != subStr[i])
return false;
++i;
}
return true;
}
bool StrEndsWith(const string& refStr, int startIndex, const string& subStr)
{
int i = 0;
while(subStr[i] != 0)
{
if (refStr[startIndex + i] != subStr[i])
return false;
++i;
}
return true;
}
bool CanFindMatch(const string& refStr, const FragmentsPair& fragmentsPair)
{
size_t cnt = fragmentsPair.m_shorterStrs->size();
vector<string*>* longerParts;
size_t shorterPartsEndIndex, longerPartsStartIndex;
if (fragmentsPair.m_longerStrs)
{
longerParts = fragmentsPair.m_longerStrs;
shorterPartsEndIndex = cnt;
longerPartsStartIndex = 0;
}
else
{
longerParts = fragmentsPair.m_shorterStrs;
shorterPartsEndIndex = cnt/2;
longerPartsStartIndex = cnt/2;
}
set<int> handledLonger;
for (size_t i = 0; i < shorterPartsEndIndex; ++i)
{
string* shorterStr = (*fragmentsPair.m_shorterStrs)[i];
bool found = false;
for (size_t j = longerPartsStartIndex; j < cnt; ++j)
{
if (handledLonger.find(j) != handledLonger.end())
continue;
string* longerStr = (*longerParts)[j];
if (StrStartsWith(refStr, *shorterStr))
{
if (StrEndsWith(refStr, fragmentsPair.m_shorterlen, *longerStr))
{
handledLonger.insert(j);
found = true;
break;
}
}
else if (StrStartsWith(refStr, *longerStr))
{
if (StrEndsWith(refStr, fragmentsPair.m_longerLen, *shorterStr))
{
handledLonger.insert(j);
found = true;
break;
}
}
}
if (!found)
return false;
}
return true;
}
bool CanBeOriginalFile(const string& refStr, const vector<FragmentsPair>& fragmentsPairs, int startPointIndex)
{
size_t totalPairs = fragmentsPairs.size();
for (size_t j = 0; j < totalPairs; ++j)
{
if (j == startPointIndex)
continue;
if (!CanFindMatch(refStr, fragmentsPairs[j]))
return false;
}
return true;
}
bool CanPairOfStrsBeOriginalFile(const string& shorterStr, const string& longerStr,
const vector<FragmentsPair>& fragmentsPairs, int startPointIndex, string& mergedStr)
{
mergedStr = shorterStr;
mergedStr.append(longerStr);
if (CanBeOriginalFile(mergedStr, fragmentsPairs, startPointIndex))
return true;
mergedStr = longerStr;
mergedStr.append(shorterStr);
return CanBeOriginalFile(mergedStr, fragmentsPairs, startPointIndex);
}
void Repair(const vector<FragmentsPair>& fragmentsPairs, int startPointIndex, string& originalFile)
{
const FragmentsPair& startPointPair = fragmentsPairs[startPointIndex];
size_t cnt = startPointPair.m_shorterStrs->size();
vector<string*>* longerParts;
size_t shorterPartsEndIndex, longerPartsStartIndex;
if (startPointPair.m_longerStrs)
{
longerParts = startPointPair.m_longerStrs;
shorterPartsEndIndex = cnt;
longerPartsStartIndex = 0;
}
else
{
longerParts = startPointPair.m_shorterStrs;
shorterPartsEndIndex = cnt/2;
longerPartsStartIndex = cnt/2;
}
for (size_t i = 0; i < shorterPartsEndIndex; ++i)
{
string* shorterStr = (*startPointPair.m_shorterStrs)[i];
for (size_t j = longerPartsStartIndex; j < cnt; ++j)
{
string mergedStr;
if (CanPairOfStrsBeOriginalFile(*shorterStr,
*((*longerParts)[j]),
fragmentsPairs, startPointIndex, mergedStr))
{
originalFile = mergedStr;
return;
}
}
}
}
void Reorganize(string& originalFile)
{
int targetLen, totalCnt;
size_t minSameLenFragmentsCnt;
Preparation(targetLen, totalCnt, minSameLenFragmentsCnt);
vector<FragmentsPair> fragmentsPairs;
int startPointIndex;
GetFragmentsPairs(targetLen, totalCnt, minSameLenFragmentsCnt, fragmentsPairs, startPointIndex);
Repair(fragmentsPairs, startPointIndex, originalFile);
}
private:
map<int, vector<string*>*> m_fragments;
};
static void RunTestCase()
{
Fragments fragments;
while(true)
{
if (!fgets(s_buf, MAX_CHAR_IN_LINE, stdin))
break;
if ((s_buf[0] == '\0') || (s_buf[0] == '\n'))
break;
int len = strlen(s_buf);
if (s_buf[len - 1] == '\n')
{
s_buf[len - 1] = '\0';
len -= 1;
}
fragments.AddFragment(s_buf, len);
}
fragments.OutputOriginalFile();
}
static void RunTests()
{
int testCaseCnt;
cin >> testCaseCnt;
fgets(s_buf, MAX_CHAR_IN_LINE, stdin); // Consume the '\n' right after the test case count.
fgets(s_buf, MAX_CHAR_IN_LINE, stdin); // Consume the empty line after the test case count.
for (int i = 0; i < testCaseCnt; ++i)
{
RunTestCase();
if (i < (testCaseCnt - 1))
cout << endl;
}
}
int main(int argc, char* argv[])
{
RunTests();
return 0;
}