[NOIP2000 提高组] 单词接龙
题目链接
提示
由于题目比较古老,所以题目中的部分表述不清楚甚至存在误导的现象。以下是我参考了各神犇的题解之后,自己对题目的理解,如有错误或者疏漏之处还请指正。
- 单词相连,是根据最小重叠部分来进行连接的,即使单词间存在包含的现象,也有可能相连,比如,
asa
和asaasa
,是直接在a
处相连的,结果应该是asasaasa
,在该题中,这样的两个词并不是 “包含” 关系,注意“包含”时应判定为false
。 - 每个单词最多只能用两次。
- 题目说的是相邻部分不能“包含”,所以龙头的部分也有可能“包含”尾巴。
不过我写的时候没注意这个好像也AC了
解题思路
如果上述都理解了,而且你对深搜和广搜有一定的基础,那么这道题的思路就已经很清晰了,就是使用DFS对所有可能的情况进行列举,最后输出最长单词的长度。唯一需要注意的点就是判断两个单词的重叠部分并将它们合并,这也是这道题的难点。
代码的书写
我们首先需要定义需要用到的变量,个人的习惯如下:
#include <iostream>
#include <cstring>
using namespace std;
const int maxn = 25;
string strs[maxn]; // 用于储存输入的单词
int cnt[maxn]; // 用于判断每个单词的使用次数
int n, ans = 0; // 这里定义为全局变量,实际根据个人代码习惯而定
接下来就需要给出判断单词重叠部分的函数了
// 这里传入的是引用,因为我直接将单词在函数内进行了合并
bool Check(int idx, string &now)
{
int len1 = now.length();
int len2 = strs[idx].length();
int i, j;
/* 从龙头的最末尾开始,注意退出条件,当i==0时,若其中的if条件句成立,
那么就说明尾巴包含龙头;若不成立,那么还是没找到匹配项,两者的结果都是false */
for(i = len1 - 1; i > 0; )
{
if(now[i] == strs[idx][0]) // 匹配到相同的字母
{
/* 注意在逐字匹配时不要让i越界,同时如果j==len2-1,同理,
龙头也有可能包含尾巴,但是注意不能直接在for循环的条件里跳出,
不然会返回true导致后面出错 */
for(j = 0; j < len2 && i <= len1 - 1; j++)
{
if(j >= len2 - 1 || now[i++] != strs[idx][j])
return false;
}
now = now + strs[idx].substr(j); // 合并
return true;
}
else
i--; // 没匹配到就往前走
}
return false;
}
接下来是DFS的部分
bool IsOver(string now) // 判断还有没有可以合并的单词
{
for(int i = 0; i < n; i++)
{
if(cnt[i] < 2 && Check(i, now))
return false;
}
return true;
}
void DFS(string now)
{
if(IsOver(now))
{
int len = now.length();
// cout << now << endl;
ans = ans > len ? ans : len;
return;
}
for(int i = 0; i < n; i++)
{
string tmp = now;
if(cnt[i] >= 2 || !Check(i, now)) continue;
cnt[i]++;
DFS(now);
cnt[i]--; // 回溯
now = tmp;
}
}
最后是main函数中的输入部分
int main()
{
char ch;
cin >> n;
for(int i = 0; i < n; i++)
cin >> strs[i];
cin >> ch;
for(int i = 0; i < n; i++)
if(strs[i][0] == ch) // 注意处理使用次数
{
cnt[i]++;
DFS(strs[i]);
cnt[i]--;
}
cout << ans << endl;
return 0;
}
完整代码
最后,这是完整的代码
#include <iostream>
#include <cstring>
using namespace std;
const int maxn = 25;
string strs[maxn];
int cnt[maxn];
int n, ans = 0;
bool Check(int idx, string &now)
{
int len1 = now.length();
int len2 = strs[idx].length();
int i, j;
for(i = len1 - 1; i > 0; )
{
if(now[i] == strs[idx][0])
{
for(j = 0; j < len2 && i <= len1 - 1; j++)
{
if(j >= len2 - 1 || now[i++] != strs[idx][j])
return false;
}
now = now + strs[idx].substr(j);
return true;
}
else
i--;
}
return false;
}
bool IsOver(string now)
{
for(int i = 0; i < n; i++)
{
if(cnt[i] < 2 && Check(i, now))
return false;
}
return true;
}
void DFS(string now)
{
if(IsOver(now))
{
int len = now.length();
// cout << now << endl;
ans = ans > len ? ans : len;
return;
}
for(int i = 0; i < n; i++)
{
string tmp = now;
if(cnt[i] >= 2 || !Check(i, now)) continue;
cnt[i]++;
DFS(now);
cnt[i]--;
now = tmp;
}
}
int main()
{
char ch;
cin >> n;
for(int i = 0; i < n; i++)
cin >> strs[i];
cin >> ch;
for(int i = 0; i < n; i++)
if(strs[i][0] == ch)
{
cnt[i]++;
DFS(strs[i]);
cnt[i]--;
}
cout << ans << endl;
return 0;
}