P1019 单词接龙
题目
题目描述
单词接龙是一个与我们经常玩的成语接龙相类似的游戏,现在我们已知一组单词,且给定一个开头的字母,要求出以这个字母开头的最长的“龙”(每个单词都最多在“龙”中出现两次),在两个单词相连时,其重合部分合为一部分,例如 beastbeast和astonishastonish,如果接成一条龙则变为beastonishbeastonish,另外相邻的两部分不能存在包含关系,例如atat 和 atideatide 间不能相连。
输入格式
输入的第一行为一个单独的整数n (n≤20)表示单词数,以下n 行每行有一个单词,输入的最后一行为一个单个字符,表示“龙”开头的字母。你可以假定以此字母开头的“龙”一定存在.
输出格式
只需输出以此字母开头的最长的“龙”的长度
分析
暴力模拟,每次接上一个单词
判断能否接上,把string dict[indexOfA]
接到string a_
上,若成功返回1
,失败返回0
string dict[70+'a'];
string a_;
/*把`string dict[indexOfA]`接到`string a_`上
* 若成功,返回1
* 若失败,返回0
*/
int attach(int indexOfA) {
string b_ = dict[indexOfA];
int status = 0;
int minlen = 1e5;
//可以合并i个字符
//最多合并minlen个字符
for(int i=1; i<b_.length() && i<a_.length(); i++) {
int x, y;
bool OK = true;
//依次倒退检查 ,不合格就break
for(int j=a_.length()-i; j<a_.length(); j++) {
x = j;
y = j-(a_.length()-i);
if(a_[x]!=b_[y]) {
OK = false;
break;
}
}
if(OK)
minlen = min(minlen, i);
}
//如果可以合并
if(minlen!=1e5 && minlen<min(a_.length(), b_.length())) {
status = 1;
a_.append(b_, minlen, b_.length());//把b_不重复的部分接在a_上
}
return status;
}
dfs出现次数两次以下的单词,vis
记录出现次数
dfs(k)返回以dict[k]
开头最长的龙
int vis[40];
int dfs(int k) {
int maxlen = 0;
string a_copy = a_;
if(attach(k) == 1 || a_==dict[k]) {//如果可以更新
vis[k]++;//标记
maxlen = max(maxlen, (int)a_.length());
for(int i=0; i<n; i++) {//找到出现次数小于2的继续深搜
if(vis[i]<2) {
maxlen = max(maxlen, dfs(i));
}
}
vis[k]--;//复原
a_ = a_copy;
}
return maxlen;
}
AC Code
#include<iostream>
#include<vector>
#include<cstring>
using namespace std;
string dict[70+'a'];
int n;char start;
string a_;
/*把`string dict[indexOfA]`接到`string a_`上
* 若成功,返回1
* 若失败,返回0
*/
int attach(int indexOfA) {
string b_ = dict[indexOfA];
int status = 0;
int minlen = 1e5;
//可以合并i个
for(int i=1; i<b_.length() && i<a_.length(); i++) {
int x, y;
bool OK = true;
//依次检查 ,不合格就break
for(int j=a_.length()-i; j<a_.length(); j++) {
x = j;
y = j-(a_.length()-i);
if(a_[x]!=b_[y]) {
OK = false;
break;
}
}
if(OK)
minlen = min(minlen, i);
}
//如果可以合并
if(minlen!=1e5 && minlen<min(a_.length(), b_.length())) {
status = 1;
a_.append(b_, minlen, b_.length());
}
return status;
}
int vis[40];
int dfs(int k) {
int maxlen = 0;
string a_copy = a_;//备份
if(attach(k) == 1 || a_==dict[k]) {//如果可以更新
vis[k]++;//标记
maxlen = max(maxlen, (int)a_.length());
for(int i=0; i<n; i++) {//找到出现次数小于2的继续深搜
if(vis[i]<2) {
maxlen = max(maxlen, dfs(i));
}
}
vis[k]--;//复原
a_ = a_copy;
}
return maxlen;
}
int main() {
cin>>n;
for(int i=0; i<n; i++){
cin>>dict[i];
}
cin>>start;
int HelloWorldZtr = 0;
for(int i=0; i<n; i++) {
if(dict[i][0] == start) {//判断dict[i]是否是以start开头
a_ = dict[i];
vis[i]++;
HelloWorldZtr = max(HelloWorldZtr, dfs(i));
vis[i]--;
}
}
cout<<HelloWorldZtr<<endl;
return 0;
}
重点
1.判断单词最大重复部分
for(int i=1; i<b_.length() && i<a_.length(); i++) {
int x, y;
bool OK = true;
//依次检查 ,不合格就break
for(int j=a_.length()-i; j<a_.length(); j++) {
x = j;
y = j-(a_.length()-i);
if(a_[x]!=b_[y]) {
OK = false;
break;
}
}
if(OK)
minlen = min(minlen, i);
}
2.将b的一部分接到a后面
= a_.append(b_.substr(minlen, b_.length() ) );
a_.append(b_, minlen, b_.length());
3.dfs的正确玩法