P1019 单词接龙 题解(dfs 深度优先搜索)

博客解析了P1019单词接龙问题,利用DFS寻找以特定字母开头的最长单词序列。题目要求单词序列中每个单词最多出现两次,且相邻单词不能有包含关系。博主提供了解题思路和代码实现。
摘要由CSDN通过智能技术生成

题目

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

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

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

输入输出样例
输入样例#1:

5
at
touch
cheat
choose
tact
a

输出样例#1:

23

分析

题目大意

题目很好理解,单词接龙,需要注意的是题目给出了“龙头”,并且这个龙头是真的,也就是说需要按照这个龙头去找第一个单词,不然可能通过不了。

解题思路

每个单词可以使用两次,所以需要一个数组去存储每个单词被访问的次数,作为递归边界的判定依据。根据题意,我们还需要知道每个单词后缀与每个单词前缀重叠的最小长度,这里是单独写了一个函数,在每次遍历的时候进行调用。其余就是dfs的事了,具体细节可以见我的代码注释。

代码

//P1019
#include<iostream>
#include<string>
#include<algorithm>
using namespace std;

const int M = 20+5;
int n;
string words[M];
int vis[M]; //每个单词的访问次数
int ans = 0;

int cmp(string a,string b){
	int l = (int)min(a.length(), b.length());
	for(int i = 1; i < l ; i++) { //重叠的个数,一定是比较短的单词长度小1,避免其被包含 
        int j;
        int flag = 1;
        for(int j = 0; j < i; j++)
            if(a[a.length() - i + j] != b[j]) //a的尾端与b首段相比较
				flag = 0;
        if(flag) 
			return i;
    }
    return 0;
}

void dfs(string s,int l){
	ans = max(ans,l); //更新最大值
	for(int j = 0;j < n;j ++){
		if(vis[j] >= 2){ //每个单词的使用次数不能超过2次
			continue;
		}	
		int c = cmp(s,words[j]); //取得最小重叠次数
		if(c > 0){
			vis[j]++;
			dfs(words[j],l+words[j].length()-c); //继续搜索下一个单词,长度需要减去重叠的个数
			vis[j]--;
		}
	}
}

int  main(){

	cin>>n;
	for(int i = 0;i < n+1;i++){ //最后一位是龙头 
		cin>>words[i];
	}
	
	dfs(" "+words[n],1); //利用龙头去找第一个,左边的空格只要不是英文字母就可以,目的是便于寻找龙头后面的一个单词,这是因为其余单词都不能互相包含 
	
	cout<<ans<<endl;
	
	return 0;
} 

传送门

P1019

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值