题目描述
给定一些字符串(只包含小写字母),要求将他们串起来构成一个字典序最小的字符串。
输入格式
第一行T,表示有T组数据。
接下来T组数据
每组第一行一个正整数n,表示字符串个数。
接下来n行,每行一个字符串(长度不超过100)。
输出格式
T行,每行一个字符串。
数据规模和约定
T<=7000,n<=100;
样例输入
1
3
a
b
c
样例输出
abc
思路
对于整个链接过程,我们需要先的到所有的可用字符串后在进行排序链接,所以简单的排序
无法解决该问题。
首先思考字符串大小比较算法。如果第一位相同,则看第二位,以此类推如果其中一个达到
最后一位只需要对该字符串长度取余即可。于是便可得到比较字符串函数check();
接着思考如果只用两个栈维护结果。那么就是每次来一个新的字符串x,那么我们会与栈顶
元素进行比较q.top()。如果x>q.top();那么直接压入一个x即可,反之则将栈顶元素弹入
预备栈s中。知到找到比x大的字符串或者q中没有字符串,将x压入栈中,并将s中的元素依
次放回,那么便可使得整体字典序最小。(该方法可通过90%)
最后便是最终的优化:
首先上述方法,当数据较为极限时,每次需要将全部元素取出,压入新来的元素,再将全部
元素放回,如此时间复杂度将会非常高。通过思考,我们发现,首字母小的一定在前面。
所以我们可以开26个栈,第0个栈存放开头是a的所有元素,第1个栈存开头是b的所有元素,
以此类推。从而我们每次插入时间只需找到首字母相同的栈进行排序即可。这样插入操作的
时间复杂度便可得到有效的控制。便得到最终结果。(Accept!)
(单调栈) $O(n^2/2)$
时间复杂度分析
优化后如果全部元素首字母都相同,最坏情况下,第2次插入取出1个元素,第3次去2个以此类推
当n为100时,最坏情况下与优化前复杂度相同,为1+2+……+99=4950≈5000,最终复杂度为5000*T
=3.5e7。可以有效accept。
C++ 代码
#include <cstring>
#include <algorithm>
#include <iostream>
#include <stack>
using namespace std;
const int N=110;
stack<string> q[27];
stack<string> s;
bool check(string x,string y)
{
int n=x.size(),m=y.size();
int k=max(n,m);
for(int i=0;i<k;i++)
{
if(x[i%n]<y[i%m]) return true;
if(x[i%n]>y[i%m]) return false;
}
return false;
}
void handel(stack<string> &q,stack<string> &s,string x)
{
if(!q.size())
{
q.push(x);
return;
}
string str=q.top();
while(!check(x,str) and q.size())
{
q.pop();
s.push(str);
if(q.size()) str=q.top();
else break;
}
q.push(x);
while(s.size())
{
string n=s.top();
s.pop();
q.push(n);
}
}
int main()
{
int T;
cin>>T;
while(T--)
{
int n;
cin>>n;
string str0;
for(int i=0;i<n;i++)
{
cin>>str0;
int t=str0[0]-'a';
handel(q[t],s,str0);
}
string res;
for(int i=0;i<26;i++)
{
while(q[i].size())
{
res=res+q[i].top();
q[i].pop();
}
}
cout<<res<<endl;
}
return 0;
}