给你一个字符串,要求你设计一种键盘,这个键盘用一行表示,其中在题目字符串中相邻的字符,在你的键盘上也要相邻。
我们用三个数组模拟一个双向链表,pre[i]表示i字符前一个字符,nxt[i]表示i字符后一个字符,vis[i]标记i字符是否在链表中出现。
输出的时候我们先把链表输出,再把剩余的随便输出就i行。
一个字符在链表出现过后,它的位置就是固定了的,如果前后为空可以添加,但不能使它调到别的地方。不能出现循环链表。
因为输出的一行,某两个相邻的一定会被拆到链表两头,就不相邻了。
具体细节在代码注释里。
注意特判输入只有一个字符的情况。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=205;
char s[N];
int pre[31],nxt[31],vis[31];
int main()
{
int t;
cin>>t;
while(t--)
{
cin>>s;
for(int i=0;i<26;i++) pre[i]=nxt[i]=-1,vis[i]=0;//初始化,-1代表前后没有字符,0代表没有出现过
int l=strlen(s),flag=1;
if(l==1)
{
printf("YES\n");
for(int i=0;i<26;i++) printf("%c",i+'a');
printf("\n");
continue;
}
vis[s[0]-'a']=1;//先把第一个字符标记上
for(int i=1;i<l&&flag;i++)//从第二个字符开始,防止越界
{
int pree=s[i-1]-'a',tmp=s[i]-'a';//tmp表示当前字符,pree表示当前字符在输入中的前一个字符
if(pre[pree]==tmp||nxt[pree]==tmp) continue;//满足题目要求,不需要再进行了
flag=0;//标记字符是否成功放进链表
if(vis[tmp]) break;//pree一定已经在链表中了,此时tmp也在链表中
//一种情况tmp两端都有元素,上面已经判断过,两端是pree的已经continue了,到这里说明不是
//一种情况tmp一端有元素,另一端为空,如果让空的这端与pree相连,就成循环了
//链表中不可能两端都没有元素
if(nxt[pree]==-1)//tmp加在pree后面
{
nxt[pree]=tmp;
pre[tmp]=pree;
flag=1;
}
if(flag==0&&pre[pree]==-1)//tmp加在pre前面
{
pre[pree]=tmp;
nxt[tmp]=pree;
flag=1;
}
if(flag==1) vis[tmp]=1;//添加成功,标记在链表中出现了
}
if(flag==0) cout<<"NO"<<endl;//有节点添加不进去
else
{
cout<<"YES"<<endl;
for(int i=0;i<26;i++)
{
if(pre[i]==-1&&nxt[i]!=-1)//找到链表头
{
int x=i;
while(x!=-1) //输出链表
{
printf("%c",x+'a');
x=nxt[x];
}
break;
}
}
for(int i=0;i<26;i++) if(!vis[i]) printf("%c",i+'a');
cout<<endl;
}
}
//system("pause");
}