单词接龙
题目描述
核心思路
- 通过贪心的思想,两个字符串拼接,重合部分越短,拼接后的长度越长,因此,需要预处理出所有单词两两拼接重合的最小长度, g [ i ] [ j ] g[i][j] g[i][j]表示下标i对应单词和下标j对应单词它俩的重叠部分的最小长度是 g [ i ] [ j ] g[i][j] g[i][j]。
- 从给定的字符开始,确定单词龙的第一个单词,向所有能够拼接到第一个单词后面的单词进行深度优先遍历。
- d f s ( d r a g o n , l a s t ) dfs(dragon,last) dfs(dragon,last), d r a g o n dragon dragon表示当前的单词龙, l a s t last last表示上一个连接到字符串的单词。
如何理解dragon+word[i].substr(k)呢?
代码
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int N=25;
int n;
//用来存储N个单词
string word[N];
//g[i][j]表示下标i对应单词和下标j对应单词它俩的重叠部分的最小长度是g[i][j]
int g[N][N];
int used[N]; //用来记录某个单词被使用了多少次
int ans;//以某个字母开头的最长的“龙”的长度。
void dfs(string dragon,int last)
{
ans=max(ans,(int)dragon.size());
//dragon表示当前的单词龙,last表示上一个连接到字符串的单词
//枚举这n个单词,找到可以接龙的单词
for(int i=0;i<n;i++)
{
//g[last][i]表示last对应单词和i对应的单词的重叠部分的最小长度,必须大于0
//used[i]=0,1表示用了一次、两次,当used[i]>=2时,表示用了超过两次了
if(g[last][i]>0&&used[i]<2)
{
int k=g[last][i];//获取last对应单词和i对应的单词的重叠部分的最小长度
used[i]++; //此时下标i所对应的单词,被使用了
//(原来已有的接龙单词)加上(i下标所对应的单词中去掉重叠部分后剩余的子串)
//然后再以当前下标i所对应的单词去递归
dfs(dragon+word[i].substr(k),i);
//恢复现场
used[i]--;
}
}
}
int main()
{
scanf("%d",&n);
//输入n个单词
for(int i=0;i<n;i++)
cin >>word[i];
char start; //一个单个字符,表示“龙”开头的字母。
cin >>start;
//预处理出所有单词两两拼接重合的最小长度
for(int i=0;i<n;i++)
{
for(int j=0;j<n;j++)
{
string a=word[i]; //下标i所对应的单词a
string b=word[j]; //下标j所对应的单词b
int lenA=a.size();
int lenB=b.size();
//枚举单词a和单词b的重合部分的最小长度
//由于但其长度必须大于等于1,且严格小于两个串的长度,所以最小从1开始枚举,最大是单词a的单词b这两个单词中
//长度最小的那个单词
for(int k=1;k<min(lenA,lenB);k++)
{
//单词a的后缀和单词b的前缀,是否相等
if(a.substr(lenA-k,lenA)==b.substr(0,k))
{
g[i][j]=k;
//由于我们是从最小长度1开始枚举,所以一旦枚举到它俩有重合部分,那么长度一定是最小的
//直接break,不用继续枚举了
break;
}
}
}
}
//dfs这n个单词
for(int i=0;i<n;i++)
{
if(word[i][0]==start) //找到某个单词的首字母是start的,然后从这个单词开始dfs
{
used[i]++;//此时下标i所对应的单词,被使用了
dfs(word[i],i);
used[i]--;//恢复现场
}
}
printf("%d\n",ans); //输出答案
return 0;
}