洛谷:P1019 [NOIP2000 提高组] 单词接龙

题目背景

注意:本题为上古 NOIP 原题,不保证存在靠谱的做法能通过该数据范围下的所有数据。

题目描述

单词接龙是一个与我们经常玩的成语接龙相类似的游戏,现在我们已知一组单词,且给定一个开头的字母,要求出以这个字母开头的最长的“龙”(每个单词都最多在“龙”中出现两次),在两个单词相连时,其重合部分合为一部分,例如 beast 和 astonish,如果接成一条龙则变为 beastonish,另外相邻的两部分不能存在包含关系,例如 at 和 atide 间不能相连。

输入格式

输入的第一行为一个单独的整数 nn 表示单词数,以下 nn 行每行有一个单词,输入的最后一行为一个单个字符,表示“龙”开头的字母。你可以假定以此字母开头的“龙”一定存在。

输出格式

只需输出以此字母开头的最长的“龙”的长度。

输入输出样例

输入:

5
at
touch
cheat
choose
tact
a

输出:23

说明/提示

样例解释:连成的“龙”为 atoucheatactactouchoose

n≤20。


分析

我们主要的问题有:

1、如何判断两个字符串是否可以连接在一起。

2、如何判断包含关系(后面我们会发现,一个函数可以同时实现上面两个问题)。

3、如何记录长度。

我们将其分为两个函数实现:

1、 fun函数:  (功能:是否可以连接,以及返回重合长度)

代码如下(示例):

//看重叠部分为多少
int fun(string s1,string s2){
	int sLen1=s1.size() ,//存串长
		sLen2=s2.size() ,
		overlap=0;	//重叠部分的长度
	
	/*i标志s1的串尾位置,j标志s2的串头位置*/
	for(int i=sLen1-1;i>0;i--){	//s1向前
		int j=0;
		if(s1[i]==s2[0]){	//当找到了连接处,在s1的i位置
		
			for(int k=i;k<sLen1;k++,j++){	//在连接处k、j向后移动(k为s1的位置,j为s2的位置)
				if(s1[k]==s2[j]) overlap++;	//相同就将重叠部分长度+1
				else {						//否则跳出本轮循环
					overlap=0;
					break;
				}
			}
			break;
			//此处的break不能忘记,一开始我就是忘记跳出,导致结果错误
		}
	}
	if(overlap==sLen1 || overlap==sLen2) return 0;//如果重叠部分等于其中一个串长,那么就是包含关系
	return overlap;
}

2、 backtracking函数:(回溯:可参考DFS算法)

代码如下(示例):

//回溯法
void backtracking(int x){
	
	for(int i=0;i<n;i++){
		int overlap=fun(s[x].word,s[i].word);
		if(s[i].useNumber<2 && overlap) //使用次数小于2 且 有重叠
		{
			cout<<s[i].word<<endl;
			s[i].useNumber++;	//次数+1
			longer += (s[i].word.size() - overlap);
			backtracking(i);
			s[i].useNumber--;
			longer -= (s[i].word.size() - overlap);
		}
	}
	longest=max(longer,longest);
}

全部代码:

代码如下(示例):

#include <bits/stdc++.h>
#include <string>
#define Max 30
using namespace std;

int n;	//单词个数
char headWord;	//龙头
int longest=0;	//最长龙
int longer=0;

struct words{	//结构体:包含单词和使用次数
	string word;
	int useNumber;
}s[Max];

void backtracking(int i);
int fun(string s1,string s2);

int main() {
	cout<<fun("envelope","envelope");
	cin>>n;
	for(int i=0;i<n;i++) {
		cin>>s[i].word;
		s[i].useNumber=0;
	}
	cin>>headWord;
	
	for(int i=0;i<n;i++){
		longer=0;
		if(headWord == s[i].word[0]) 
		{
			s[i].useNumber++;
			longer += s[i].word.size();
			backtracking(i);
			s[i].useNumber--;
			longer -= s[i].word.size();
		}
	}
	cout<<longest;
}

//回溯法
void backtracking(int x){
	
	for(int i=0;i<n;i++){
		int overlap=fun(s[x].word,s[i].word);
		if(s[i].useNumber<2 && overlap) //使用次数小于2 且 有重叠
		{
			cout<<s[i].word<<endl;
			s[i].useNumber++;	//次数+1
			longer += (s[i].word.size() - overlap);
			backtracking(i);
			s[i].useNumber--;
			longer -= (s[i].word.size() - overlap);
		}
	}
	longest=max(longer,longest);
}

//看重叠部分为多少
int fun(string s1,string s2){
	int sLen1=s1.size() ,//存串长
		sLen2=s2.size() ,
		overlap=0;	//重叠部分的长度
	
	/*i标志s1的串尾位置,j标志s2的串头位置*/
	for(int i=sLen1-1;i>0;i--){	//s1向前
		int j=0;
		if(s1[i]==s2[0]){	//当找到了连接处,在s1的i位置
		
			for(int k=i;k<sLen1;k++,j++){	//在连接处k、j向后移动(k为s1的位置,j为s2的位置)
				if(s1[k]==s2[j]) overlap++;	//相同就将重叠部分长度+1
				else {						//否则跳出本轮循环
					overlap=0;
					break;
				}
			}
			break;
		}
		
	}
	if(overlap==sLen1 || overlap==sLen2) return 0;//如果重叠部分等于其中一个串长,那么就是包含关系
	return overlap;
}

你悟解了吗?

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

悟解了

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值