SDNUOJ 1022 成语接龙
Problem - 1022 (rainng.com)(传送门)
输入数字n,然后输入n个单词,输入一个字母,求出以此字母开头最长的龙的长度
Sample Input:
5
at
touch
cheat
choose
tact
a
Sample Output:
23
先写上AC码:
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int N=28;
int n;//单词的个数n
string s[N];//存储输入的n个单词
int g[N][N];//标记n个单词间存在的路径
int used[N];//标记每个单词的使用次数(最多两次)
int ans=-1;//最长路径的长度
void dfs(string str,int cur)
{
ans=max(ans,(int)str.size());
for(int i=0;i<n;i++)
{
if(g[cur][i]&&used[i]<2)
{
used[i]++;
dfs(str+s[i].substr(g[cur][i]),i);
used[i]--;
}
}
}
int main()
{
cin>>n;
for(int i=0;i<n;i++)
{
cin>>s[i];
}
for(int i=0;i<n;i++)
{
for(int j=0;j<n;j++)
{
string a=s[i],b=s[j];
for(int k=1;k<min(a.size(),b.size());k++)
{
if(a.substr(a.size()-k)==b.substr(0,k))
{
g[i][j]=k;
break;
}
}
}
}
char c;
cin>>c;
for(int i=0;i<n;i++)
{
if(s[i][0]==c)
{
memset(used,0,sizeof used);
used[i]++;
dfs(s[i],i);
}
}
cout<<ans<<endl;
return 0;
}
思路
先遍历出每个单词可以接上哪些单词,并且标记这条路径存在,然后搜索出最长的那一条。
实现
先看主函数部分
输入单词个数n和对应的n个单词(没啥好解释的):
cin>>n;
for(int i=0;i<n;i++)
{
cin>>s[i];
}
确定存在的路径:
for(int i=0;i<n;i++)
//外层循环遍历的是路径起点
{
for(int j=0;j<n;j++)
//内层循环遍历的是*可能的*路径终点(所以下面要判别)
{
string a=s[i],b=s[j];
for(int k=1;k<min(a.size(),b.size());k++)
//遍历所有可能的重合字母数
{
if(a.substr(a.size()-k)==b.substr(0,k))
{
g[i][j]=k;
//标定从第i个词可走到第j个词,重合k个字母
break;
}
}
}
}
输入龙的起始字母:
char c;
cin>>c;
寻找到最长的龙并输出其长度:
for(int i=0;i<n;i++)
{
if(s[i][0]==c)
//如果第i个词的头是起始字母就以之为起点找路
{
memset(used,0,sizeof used);
//清零每个单词的使用次数(因为会遍历多条路径)
used[i]++;
//第i个单词作为起点了,所以它的使用次数先加一
dfs(s[i],i);//寻找路径
}
}
cout<<ans<<endl;//输出最长路径的长度
return 0;
然后看这里面的dfs函数部分:
void dfs(string str,int cur)
//联系上面,cur是单词的序号
{
ans=max(ans,(int)str.size());
//因为要寻找到最长的路径,所以龙尾加新词后要max一下
for(int i=0;i<n;i++)//遍历全部可能的单词
{
if(g[cur][i]&&used[i]<2)
//如果可以从第cur个词(也就是进入函数的词)到第i个词
{
used[i]++;//第i个词使用次数加一
dfs(str+s[i].substr(g[cur][i]),i);
//去以第i个单词为路径寻找下一个词
used[i]--;
//这一步看似反逻辑,但是是为了去寻找另外的路径
//因为此时函数嵌套已经结束,代表着这条路已经到底了
}
}
}//这里用void是因为函数外已经宏定义ans了,直接操作不用return
至此,题目的思路已经很明了了。