【分割问题】-单词接龙

信息学奥赛一本通(C++版)在线评测系统


解题思路:

(1)本题的细节处理比较多,题意比较好理解,但是理解容易出错

(2)首先,给定的单词可以利用一个string数组来存储,然后输入首字母,第一步应该是在所有单词中,找到以这个字符开头的单词,作为龙的第一项,然后开始dfs深搜

(3)因为每个单词最多可以使用2次,所以建立一个桶数组,用来标记每个单词使用的次数即可,其次单词拼接不能存在包含关系(本人理解错误意思),最后单词必须有重合的部分才可以接下去,并且重合的部分不算长度。

(4)因为要找所有情况中的最佳方案,那么需要用到回溯,直接利用计数的桶数组回溯即可

(5)dfs(string a,int len)记录此时龙的末尾单词,因为要找下一个可以拼接的单词,所以必须得保存这个单词,并且记录此时的长度,每次都保存好,用来求最大值。再进行深搜的时候,无非就是又把所有的单词重新组合一遍,看哪些可以组合,然后加上长度,注意:这里的长度是需要减掉重合部分的。

(6)那么可以设置一个check函数来判断两个字符串的重合长度,因为理解错意思,题目中的相连的两个单词不能产生包含关系,所以开头就直接判断了是否包含,导致没有AC,其实题目的意思为,如果不能使龙的长度增加,则不能包含,因为包含进去就是重合的部分,和没加一样(去你码的,谁能想到)比如ata和atatata是可以相连的

(7)还有一个问题,当找到重合的字母的时候,到底要找到最长的公共部分,还是找到越短的呢?比如上一个是aa下一个单词是aaaaa,如果按照最短的拼接,形成的长度为六个a,如果按照公共的长度,那么还是5个a(其他博主说是6个a,只要找到一个首字母即可,有待钻研,AC代码是按照最短的来,找到了一个相同的,return i即可)

 (8)然后就是dfs函数,等到所有的情况枚举完之后,便退出,每次深入一层,就取长度的最大值即可,注意这里的可行性方案为单词有重合,而且单词不能使用超过两次。


#include<bits/stdc++.h>
using namespace std;

int n,ans;
char ch;//表示首字母 
string a[30];//存入字符串 
int vis[30];//桶数组标记 

int check(string s1,string s2)
{
	
	int len1=s1.length();//获取之前字符串的长度 
	int len2=s2.length();//获取待接的字符串的长度 
	int mine=min(len1,len2);//求出最短长度 
	
	for(int i=1;i<=mine;i++)
	{
		string ss1=s1.substr(len1-i,i);//截取尾部的字符串 
		string ss2=s2.substr(0,i);//截取首部的字符串 
		if(ss1==ss2)//如果这两个字符串相等 
		return i;//返回重合的长度 
	}
	
	return 0;//如果没有找到重合的,返回0 
}

void dfs(string s,int len)
{ 
	ans=max(ans,len);//取最长的龙
	
	for(int i=1;i<=n;i++)
	{
		if(check(s,a[i])!=0&&vis[i]<2)//可行性方案,如果可以连接并且单词没有使用两次以上 
		{
			vis[i]++;//使用该单词数量+1,记录现场 
			dfs(a[i],len+a[i].length()-check(s,a[i]));//长度增加a[i]的长度,并且减去重合部分 
			vis[i]--;//恢复现场 
		}
	}
}

int main()
{
	cin>>n;
	for(int i=1;i<=n;i++)
	cin>>a[i];
	cin>>ch;//输入数据 
	
	for(int i=1;i<=n;i++)//遍历第一个可以连接的单词 
	{
		string ss=a[i];
		if(ss[0]==ch)//如果首字母和给定字符相同
		{
			memset(vis,0,sizeof(vis));//每次清空桶数组 
			vis[i]=1;//第i个单词用一次 
			dfs(ss,ss.length());//开始搜索 
			vis[i]=0;//回溯 
		}
	}
	
	cout<<ans; 
	return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值