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

P1019 单词接龙

  • 题目
  • 分析
    • 题目大意
    • 解题思路
  • 代码
  • 传送门

题目

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

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

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

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

5
at
touch
cheat
choose
tact
a

输出样例#1:

23

分析

题目大意

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

解题思路

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

代码

//P1019
#include
#include
#include
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

你可能感兴趣的:(P1019 单词接龙 题解(dfs 深度优先搜索))