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

it2022-05-09  37

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<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)