洛谷 P1019 单词接龙

本文围绕单词接龙游戏展开,给出题目描述、输入输出格式及样例。解题基本思路是搜索,难点在于处理单词重叠部分。介绍了用check函数比较接口匹配的方法,还提及一些细节,如舍去非最优接龙方案、拼接用串副本等,最后说明了具体操作步骤。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目描述

单词接龙是一个与我们经常玩的成语接龙相类似的游戏,现在我们已知一组单词,且给定一个开头的字母,要求出以这个字母开头的最长的“龙”(每个单词都最多在“龙”中出现两次),在两个单词相连时,其重合部分合为一部分,例如 beastbeast和astonishastonish,如果接成一条龙则变为beastonishbeastonish,另外相邻的两部分不能存在包含关系,例如atat 和 atideatide 间不能相连。

输入输出格式

输入格式:

 

输入的第一行为一个单独的整数nn (n \le 20n≤20)表示单词数,以下nn 行每行有一个单词,输入的最后一行为一个单个字符,表示“龙”开头的字母。你可以假定以此字母开头的“龙”一定存在.

 

输出格式:

 

只需输出以此字母开头的最长的“龙”的长度

 

输入输出样例

输入样例#1: 

5
at
touch
cheat
choose
tact
a

输出样例#1: 

23

说明

(连成的“龙”为atoucheatactactouchoose)

 

思路截取自洛谷题解https://www.luogu.org/problemnew/solution/P1019

基本思路是搜索。

处理的难点在于对重叠部分的处理。

单词的使用次数很好判断,开一个数组即可,和正常向dfs的vis数组差不多。

但对于重叠部分的处理,我想细说一下。

因为连接起来的单词要最长,所以对比是选择从上一个单词的末尾与当前单词的开头进行比对,如果发现不符那就不能匹配。

这里我借鉴了一位大神的思路,使用一个check函数,用来比较两个串s和m的长度为k的接口能不能匹配。

判断方式有两种,第一种就是我用的这样按字符比较,如果发现某处不匹配立即返回false。还有一个方法是用string里面的substr,我就不展开了,有兴趣的同学可以试一下。

我们假设接口长度为k,串s的长度为lens,然后我们从0到k枚举,判断s[lens-k+i]是不是等于m[i]。

这个式子怎么来的呢?接口前面的串是s,后面的串是m,那么很显然,s串的接口最开始应该是lens-k处,然后在后面加上一个枚举的i就可以保证扫描到所有接口字符(我们的i是从0开始枚举的)。

还有一些细节问题。 如果我们在接龙的时候发现我们现在要接的龙还不如之前某一次接过的长,那么这个接龙方案肯定不是最优的,所以要舍去,这个正确性是显然的。

(想一下深搜时的遍历过程,这句话便不难理解)

使用我这个方法,可能有一个奇怪的想法有同学没想到,那就是在进行拼接操作时,要注意使用给定的串的副本(即复制一份原来的串)进行拼接处理。

这是因为,如果你把原串改变了,而且这个串还不是最优的,那就完了,回溯不回去了。

具体到操作上,首先,我们从1到n枚举每个短字符串,如果它已经被用了两次则continue,然后我们求出当前短串的长度,从1到这个长度枚举,枚举的是接口的长度(自然是接口越短融合串越长嘛) 然后执行拼接操作,记录一下最大长度,再加上回溯就好了。

拼接操作和check函数很像,具体到代码上大家就能看明白了。

 

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;

string words[25],begin;
int ans,n,used[25];

bool check(string a,string b,int k)
{
	int len=a.length();
	for(int i=0;i<k;i++)
	{
		if(a[len-k+i]!=b[i]) return false;
	}
	return true;
}

void append(string &a,string b,int k)
{
	int len=b.length();
	for(int i=k;i<len;i++)
	{
		a+=b[i];
	}
}

void dfs(string nowString)
{
	int nowLen=nowString.length();
	ans=max(nowLen,ans);
	for(int i=0;i<n;i++)
	{
		if(used[i]>=2) continue;
		int maxk=words[i].length();
		for(int j=1;j<maxk;j++)
		{
			if(check(nowString,words[i],j))
			{
				string temp=nowString;
				append(temp,words[i],j);
				if(temp==nowString) continue;
				
				used[i]++;
				dfs(temp);
				used[i]--;
			}
		}
	}
}



int main()
{
	scanf("%d",&n);
	for(int i=0;i<n;i++)
	{
		cin>>words[i];
	}
	cin>>begin;
	dfs(begin);
	printf("%d",ans);
	
	return 0;
} 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值