题目描述
如果单词X的末字母与单词Y的首字母相同,则X与Y可以相连成X.Y。(注意:X、Y之间是英文的句号“.”)。例如,单词dog与单词gopher,则dog与gopher可以相连成dog.gopher。
另外还有一些例子:
dog.gopher
gopher.rat
rat.tiger
aloha.aloha
arachnid.dog
连接成的词可以与其他单词相连,组成更长的词链,例如:
aloha.arachnid.dog.gopher.rat.tiger
注意到,“.”两边的字母一定是相同的。
现在给你一些单词,请你找到字典序最小的词链,使得这些单词在词链中出现且仅出现一次。
输入格式:
第一行是一个正整数n(1 ≤ n ≤ 1000),代表单词数量。
接下来共有n行,每行是一个由1到20个小写字母组成的单词输出格式:
只有一行,表示组成字典序最小的词链,若不存在则只输出三个星号“*”。
数据范围:
对于100%的数据,有n≤1000。
补昨天的博客…
首先,这是一道图论题…
这种单词接龙样子的一般都是图论题,相信大家都知(bei)道(keng)了(guo)吧…
如果以单词作为点,能连的单词建边,那好像就是一个裸的哈密顿路了?
不过连边的顺序要注意,先连字典序小的,再连字典序大的,这样能够保证找到的第一条哈密顿路就是答案。
所以要用string类型,先排序,用排名代替单词建图。
然后我就开开心心地敲了一个哈密顿路…
30分…
不光有TLE还有WA…
吓得我扫了好几遍代码,终于发现了华点…
我先把单词从小到大排序,然后对于每一个单词,先连小的再连大的…
也就是:
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
if(i!=j&&a[i][len[i]-1]==a[j][0])
add(i,j);
我天真地以为这样连就能先访问小的再访问大的…
可是因为head数组指向所连的最后一条边,所以先连的边最后访问啊!
又sb了…不过数据很水还给了三十分…
所以应该把第二层j倒着循环,改完了又交了一遍。
T了两个点..
这道题哈密顿路应该不会错啊…可是怎么优化?
经过冥(fan)思(kan)苦(ti)想(jie)后,我仿若醍醐灌顶…
可以用欧拉路来优化!
如果把二十六个字母作为点,每个单词首尾字母连一条边,那不就是跑欧拉路吗!
不过不能纯跑欧拉路,因为建边的时候不能按照字典序建边(不一定指向a就比指向b小),所以不能保证找到的第一条欧拉路就是答案。
但可以用来查找从那个点开始搜索。
因为如果不存在哈密顿路,那也就不存在欧拉路(这两个路本质上好像没什么区别),如果存在哈密顿路,那么以哈密顿路的开头单词的首字母一定能跑出欧拉路来。
所以我们就可以先判断从哪些字母出发可以跑出欧拉路,再从以这些字母为首的单词开始跑哈密顿路。
虽然理论时间复杂度好像不变,但加了这个优化就跑到12ms了。
代码如下:
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<string>
#include<cstring>
#include<cstdlib>
using namespace std;
struct edge{
int to,next;
}ed[1000001];
int head[1001]={0};
string a[1001];
int b[30]={0};
bool vl[1001]={0};
bool vis[1001]={0};
int ans[1001]={0};
int len[1001];
int size=0;
int cnt=0,n;
void add(int from,int to)
{
size++;
ed[size].to=to;
ed[size].next=head[from];
head[from]=size;
}
void print()
{
for(int i=1;i<cnt;i++)
{
cout<<a[ans[i]];
printf(".");
}
cout<<a[ans[cnt]];
exit(0);
}
void dfs(int u)
{
vl[u]=vis[u]=1;
ans[++cnt]=u;
for(int i=head[u];i;i=ed[i].next)
{
int v=ed[i].to;
if(!vis[v]) dfs(v);
}
if(cnt==n)
{
print();
}
vis[u]=0;
cnt--;
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
cin>>a[i];
}
sort(a+1,a+n+1);
for(int i=1;i<=n;i++)
{
len[i]=a[i].size();
}
for(int i=1;i<=n;i++)
{
for(int j=n;j>=1;j--)
{
if(i!=j&&a[i][len[i]-1]==a[j][0])
{
add(i,j);
}
}
}
for(int i=1;i<=n;i++)
{
b[a[i][0]-'a']++;
b[a[i][len[i]-1]-'a']--;
}
int c1=0,c_1=0,s;
for(int i=0;i<26;i++)
{
if(b[i]==1) c1++,s=i;
else if(b[i]==-1) c_1++;
}
if(c1==1&&c_1==1)
{
for(int i=1;i<=n;i++)
{
if(a[i][0]-'a'==s)
dfs(i);
}
}
else if(c1==0&&c_1==0)
{
for(int i=1;i<=n;i++)
dfs(i);
}
printf("***");
return 0;
}
这个算法好像有个漏洞,就是单纯的sort排序能否保证第一条路径就是答案?
例如abc和abcb,用string比较结果是abc小于abcb,但是如果后面接单词的话就是abc.c…和abcb.b…,显然第二个又更小了…
咸鱼的想法:前面字母都一样最后一个字母不一样几率辣么小忽略不就行辣
也许字典序并不是我理解的那样吧…反正a了就好。