词链

词 链 词链

题目链接:luogu 1127

题目

如果单词XX的末字母与单词YY的首字母相同,则XX与YY可以相连成X.YX.Y。(注意:XX、YY之间是英文的句号“.”)。例如,单词 d o g dog dog与单词 g o p h e r gopher gopher,则 d o g dog dog g o p h e gophe gopher可以相连成 d o g . g o p h e r dog.gopher dog.gopher
另外还有一些例子:

  1. d o g . g o p h e r dog.gopher dog.gopher
  2. g o p h e r . r a t gopher.rat gopher.rat
  3. r a t . t i g e r rat.tiger rat.tiger
  4. a l o h a . a l o h a aloha.aloha aloha.aloha
  5. a r a c h n i d . d o g arachnid.dog arachnid.dog

连接成的词可以与其他单词相连,组成更长的词链,例如:
a l o h a . a r a c h n i d . d o g . g o p h e r . r a t . t i g e r aloha.arachnid.dog.gopher.rat.tiger aloha.arachnid.dog.gopher.rat.tiger

注意到,“.”两边的字母一定是相同的。

现在给你一些单词,请你找到字典序最小的词链,使得这些单词在词链中出现且仅出现一次。

输入

第一行是一个正整数 n ( 1 ≤ n ≤ 1000 ) n(1≤n≤1000) n(1n1000),代表单词数量。
接下来共有 n n n行,每行是一个由 1 1 1 20 20 20个小写字母组成的单词。

输出

只有一行,表示组成字典序最小的词链,若不存在则只输出三个星号 “ ∗ ∗ ∗ ” “∗∗∗”

样例输入

6
aloha
arachnid
dog
gopher
rat
tiger

样例输出

aloha.arachnid.dog.gopher.rat.tiger

数据范围

对于 40 % 40\% 40%的数据,有 n ≤ 10 n≤10 n10
对于 100 % 100\% 100%的数据,有 n ≤ 1000 n≤1000 n1000

思路

这道题我们用 d f s dfs dfs来做。
我们可以先枚举找到可以与某字符串相连的字符串,并且统计下来。
然后判断这张图是可以连成一条线,还是可以连成环。如果都不可以,就直接输出 “ ∗ ∗ ∗ ” “***” 不存在。
如果可以连成线,我们只要从线的开头的点开始 d f s dfs dfs,就能直接找到答案;而如果可以连成环,就要枚举每一个点 d f s dfs dfs,找到方法就退出。
最后我们判断是否找到了答案,找到了就输出,不然就退出。

代码

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#define rr register
using namespace std;
int n,b[1001][1001],ans[1001],l[1001],letter[27];
bool in[1001],yes;
string a[1001];
void dfs(int now)//dfs
{
    if (yes) return ;//已经找到了
    ans[++ans[0]]=now;//标记
    in[now]=1;//标记
    for (int i=1;i<=b[now][0];i++)
    if (!in[b[now][i]])//没有到过
    dfs(b[now][i]);//dfs
    if (ans[0]==n) yes=1;//找到了
    else//没找到(回溯)
    {
        ans[0]--;
        in[now]=0;
    }
    in[now]=0;//回溯
}
int main()
{
    scanf("%d",&n);//读入
    for (rr int i=1;i<=n;i++)
    cin>>a[i];//读入
    sort(a+1,a+n+1);//按字典序从小到大排序
    for (rr int i=1;i<=n;i++)
    l[i]=a[i].size();//求出长度
    for (rr int i=1;i<=n;i++)
    for (rr int j=1;j<=n;j++)
    if (i!=j)
    if (a[i][l[i]-1]==a[j][0])
    b[i][++b[i][0]]=j;//统计可以与字符串i相连的字符串
    for (int i=1;i<=n;i++)//算出断开的部分数
    {
    	letter[a[i][0]-96]++;
    	letter[a[i][l[i]-1]-96]--;
    }
    int mei=0,bhuan;
    for (int i=1;i<=26;i++)
    {
    	if (letter[i]==1) {mei++;bhuan=i;}//只断了一处(线)
    	if (letter[i]==2||mei==2)//断两处或以上(无解)
    	{
    		printf("***");//输出
    		return 0;//退出
		}
	}
	if (mei)//只能组成一个线
	{
		for (int i=1;i<=n;i++)
		if (a[i][0]-96==bhuan)//找到线开头的点
		{
			dfs(i);//dfs
			break;//找到了,退出
		}
	}
	else for (rr int i=1;i<=n;i++)//可以组成一个环
    {
        memset(ans,0,sizeof(ans));//初始化
        dfs(i);//dfs
        if (yes) break;//已经找到词链,退出
    }
    if (yes)
    {
        for (rr int i=1;i<ans[0];i++)
        cout<<a[ans[i]]<<".";//输出
        cout<<a[ans[ans[0]]];//输出
    }
    else printf("***");//不存在的输出
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值