DFS-单词接龙题解
题解
题意:
给定\(n(n\leq20)\)个单词以及单个字符作为开头字母,求出以这个字母开头的最长的“单词接龙”的长度,每个单词最多使用两次
思路:
\(n\leq20\),根据由数据范围反推算法复杂度以及算法内容 - AcWing结合题意,很显然用dfs算法解决,结合dfs模板敲代码即可
ans
void dfs(层数,其他参数){
if(出局判断){ // 到达最底层,或者满足条件推出
更新答案 // 答案一般用全局变量表示
return; // 返回到上一层
}
(剪枝) // 在进一步dfs之前剪枝
for(枚举下一层可能的情况){
if(used[i]==0){ // 如果状态i没有用过,就可以进入下一层
used[i]=1; // 标记状态i已经使用过
dfs(层数+1,其他参数); // 下一层
used[i]=0; // 回溯
}
}
return; // 返回到上一层
}
code:
#include<bits/stdc++.h>
using namespace std;
#define debug(x) cerr << #x << " = " << x << "\n";
int n;
const int N = 25;
string g[N];
map<string,int> cnt;
//
int check(string s1,string s2){
int len1=s1.size(),len2=s2.size();
int len = min(len1,len2);
int res=0;
// debug(s1);debug(s2);
// debug(len1);debug(len2);
for(int i=1;i<len;i++){
// debug(i);
// debug(s1.substr(len1-i,i));
// debug(s2.substr(0,i));
if(s1.substr(len1-i,i)==s2.substr(0,i)){
res=i;
break;
}
}
return res;
}
int dfs(string s,int len){
cnt[s]++;
// debug(len);
if(cnt[s]>2){
return len;
}
int res=len;
for(int i=0;i<n;i++){
// int ii=i;debug(ii);
int sz = check(s,g[i]);
if(cnt[g[i]]<2 && sz>0){
res = max(res,dfs(g[i],len+g[i].size()-sz));
// debug(res);
cnt[g[i]]--;
}
}
return res;
}
int main(){
cin>>n;
for(int i=0;i<n;i++){
cin>>g[i];
cnt[g[i]]=0;
}
char ch;
cin>>ch;
int ans =0;
for(int i=0;i<n;i++){
if(g[i][0]==ch){
ans = max(ans,dfs(g[i],g[i].size()));
cnt[g[i]]--;
}
// debug(cnt[g[0]]);
// debug(cnt[g[1]]);
}
cout << ans << "\n";
return 0;
}
收获
bug1:
对于
2
at
touch
a
输入样例
为什么第31行res = 0得出的结果是0, res = len得出的结果是6,同样在里面都取了max,为什么结果会不同呢?
因为最后可能字符串s和其他的字符串都没连接上啊,那么最后要返回的就是连接到字符串s为止的字符串长度,你取0了可能最后返回的都是0,当然结果就是0了。
bug2
代码53-57行
for(int i=0;i<n;i++){
if(g[i][0]==ch){
ans = max(ans,dfs(g[i],g[i].size()));
cnt[g[i]]--;
}
有回溯的dfs结束后除初始状态,其他所有状态都会归零,对于初始状态要额外操作使其初始化,防止对以其它字符串为开头的dfs产生影响。
优化
- 求的是最大长度,我们不需要用带有int返回值的dfs,直接在每次dfs后不管是否连接到极限都更新ans = max(ans,已连接的字符串长度),这样只需无脑照板子void dfs()抄即可,不用花时间考虑int dfs()
- 预处理出每两个字符串之间可连接的最少长度,dfs加一个参数-当前所使用的字符串的序号,以便快速判断出两个字符串之间连接的最少长度。这样可以优化时间复杂度

浙公网安备 33010602011771号