洛谷千题详解 | P1019 [NOIP2000 提高组] 单词接龙【C++、Java语言】

news2024/9/30 23:38:52

博主主页:Yu·仙笙

专栏地址:洛谷千题详解

 

目录

题目描述

输入格式

输出格式

输入输出样例

 解析:

C++源码:

Java源码:


--------------------------------------------------------------------------------------------------------------------------------

 --------------------------------------------------------------------------------------------------------------------------------

题目描述

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

 --------------------------------------------------------------------------------------------------------------------------------

输入格式

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

 --------------------------------------------------------------------------------------------------------------------------------

输出格式

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

 --------------------------------------------------------------------------------------------------------------------------------

输入输出样例

输入 #1

5
at
touch
cheat
choose
tact
a

输出 #1

 --------------------------------------------------------------------------------------------------------------------------------

 解析:

谨以此题解纪念我的洛谷橙名,我会继续努力。

基本思路是搜索。

处理的难点在于对重叠部分的处理。

单词的使用次数很好判断,开一个数组即可,和正常向dfs的vis数组差不多。

但对于重叠部分的处理,我想细说一下。

因为连接起来的单词要最长,所以对比是选择从上一个单词的末尾与当前单词的开头进行比对,如果发现不符那就不能匹配。

这里我借鉴了一位大神的思路,使用一个check函数,用来比较两个串s和m的长度为k的接口能不能匹配。

判断方式有两种,第一种就是我用的这样按字符比较,如果发现某处不匹配立即返回false。还有一个方法是用string里面的substr,我就不展开了,有兴趣的同学可以试一下。

我们假设接口长度为k,串s的长度为lens,然后我们从0到k枚举,判断s[lens-k+i]是不是等于m[i]。

这个式子怎么来的呢?接口前面的串是s,后面的串是m,那么很显然,s串的接口最开始应该是lens-k处,然后在后面加上一个枚举的i就可以保证扫描到所有接口字符(我们的i是从0开始枚举的)。

还有一些细节问题。 如果我们在接龙的时候发现我们现在要接的龙还不如之前某一次接过的长,那么这个接龙方案肯定不是最优的,所以要舍去,这个正确性是显然的。

(想一下深搜时的遍历过程,这句话便不难理解)

使用我这个方法,可能有一个奇怪的想法有同学没想到,那就是在进行拼接操作时,要注意使用给定的串的副本(即复制一份原来的串)进行拼接处理。

这是因为,如果你把原串改变了,而且这个串还不是最优的,那就完了,回溯不回去了。

具体到操作上,首先,我们从1到n枚举每个短字符串,如果它已经被用了两次则continue,然后我们求出当前短串的长度,从1到这个长度枚举,枚举的是接口的长度(自然是接口越短融合串越长嘛) 然后执行拼接操作,记录一下最大长度,再加上回溯就好了。

拼接操作和check函数很像,具体到代码上大家就能看明白了。

 --------------------------------------------------------------------------------------------------------------------------------

C++源码:

#include<cstdio>
#include<iostream>
#include<string>
#include<cmath> 
using namespace std;
int n;//单词数 
string tr[30];//存储字符串 
int yc[30][30];//两个字母的最小重叠部分 
int vis[30];//判断单词使用频率. 
int mt(int x, int y){//mt函数,返回x单词后连接一个y单词的最小重叠部分 
    bool pp=true; 
    int ky=0;
    for(int k=tr[x].size()-1;k>=0;k--){//从x单词尾部向前看看最小重叠部分是从哪里开始的,以为因为是倒着来,所以保证是最小的 
        for(int kx=k;kx<tr[x].size();kx++){/ 
            if(tr[x][kx]!=tr[y][ky++]){
                pp=false;
                break;
            }
        }
        if(pp==true){//如果说当前以k为开头的前一个单词后缀 ,是后面单词的前缀,就马上返回重叠部分。(tr[x].size()-k是找出来的规律)
            return tr[x].size()-k;        } 
        ky=0;
        pp=true;//不行就继续
    }
    return 0;
}//可能这里有点难理解。可以手动模拟一下
char ch;//开头字母 
int ans=-1;//答案 
int an=0;//每次搜到的当前最长串 
void dfs(int p){//p为尾部单词编号(p的后缀就是“龙”的后缀,因为p已经连接到”龙“后面了)
    bool jx=false; 
    for(int j=1;j<=n;j++){
        if(vis[j]>=2) continue;//使用了两次就跳过 
        if(yc[p][j]==0) continue;//两单词之间没有重合部分就跳过 
        if(yc[p][j]==tr[p].size() || yc[p][j]==tr[j].size()) continue;//两者存在包含关系就跳过 
        an+=tr[j].size()-yc[p][j];//两单词合并再减去最小重合部分 
        vis[j]++;//使用了一次
        jx=true;//标记一下当前已经成功匹配到一个可以连接的部分 
        dfs(j); //接上去
        an-=tr[j].size()-yc[p][j];//回溯,就要再减回去那一部分长度 
        vis[j]--;//回溯,使用-- 
    }
    if(jx==false){//jx==false说明不能再找到任何一个单词可以相连了 
        ans=max(ans,an);//更新ans 
    }
    return;
}
int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        cin>>tr[i];
    cin>>ch; 
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++){
            yc[i][j]=mt(i,j); 
        }
    }//预处理yc数组。yc[i][j]就表示,i单词后连接一个j单词的最小重叠部分 
    //比如 i表示at,j表示att. yc[i][j]就为2 但是yc[j][i]就为0.
    //预处理是一个关键
     
    for(int i=1;i<=n;i++){//从头到尾看一下有没有以指定开头字母为开头的单词 
        if(tr[i][0]==ch){//如果有,就以当前单词为基准进行搜索。 
            vis[i]++;//使用过一次 
            an=tr[i].size();//更新当前串长度 
            dfs(i);//接上
            vis[i]=0;//消除影响 
        } 
    } 
    printf("%d",ans);
    return 0;
}
#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#define maxn 100
using namespace std;
int n;
int ans = 0;
string word[maxn];//字符串数组,用来存储单词
string beginn;//用来存储开头字符
int used[maxn];//这个就是用来记录dfs时候每一个单词被使用了几次的数组

bool check(string s,string m,int k){//重点一,check函数判断接口可行性,k代表接口长度,以下同
    int lens = s.length();
    for (int i=0;i<k;i++){
        if(s[lens-k+i]!=m[i])//我讲过了
            return false;
    }
    return true;
}

void add(string &s,string m,int k){//拼接操作,因为要把m接到s上,所以对于s串不可以传参,因为我们要试图改变这个串
    int lenm = m.length();
    for (int i=k;i<lenm;i++)
        s+=m[i];//C++字符串类型特性操作,支持+=符号直接拼接
}

void dfs(string now){//这只是一个看似平淡无奇的dfs
    int x = now.length();
    ans = max(ans,x);//每次拼接之后更新一下答案
    for (int i=1;i<=n;i++){
        if (used[i]>=2)//如果有一个单词用完了,那这个单词就不能选了
            continue;
        int maxk = word[i].length();
        for (int j=1;j<=maxk;j++){//枚举接口长度
            if (check(now,word[i],j)){
                string temp = now;//重点二,使用字符串副本进行拼接
                add(temp,word[i],j);
                if (temp==now)//拼完之后如果发现长度没增加,也就是和原串一样,那这次拼接没有意义,剪掉
                    continue;
                used[i]++;
                dfs(temp);
                used[i]--;//这只是一个看似平淡无奇的回溯
            }
        }
    }
    
}

int main(){
    cin >> n;
    for (int i=1;i<=n;i++)
        cin >> word[i];
    cin >> beginn;
    dfs(beginn);
    
    cout << ans << endl;
    return 0;
    
}

 --------------------------------------------------------------------------------------------------------------------------------

Java源码:

import java.util.Scanner;

public class P1019 {
// 要点:
//	1.单词拼接一定要相同的部分:能连接
//	2.在保证能连接的情况下,保证连接长度最长
//	3.不能存在包含关系
	static String []a=new String[30];//存储单词
	static int []use_times=new int[30];//使用次数
	static int p=0;
	static int n=0,ans=0;//N为单词数,ans是长度
	
	//用于查找两字符串是否是包含关系,或者相同的有多少个单词
	public static int  find(int index,int y) {//y为当前匹配的单词,index为龙当前的最后一个单词
		
		for(int i=a[index].length()-1;i>=1;i--) {//查找龙的最后一个单词从尾部向前搜索和匹配单词的开头相同的位置

			if(a[index].substring(i, i+1).equals(a[y].substring(0,1))) {
				
				int k=0;//设立临时值,用于单词匹配数
				for(int j=i+1;j<a[index].length();j++) {//如果龙的最后一个匹配,则直接退出,保证单词连接最长。
					k++;
					if(!a[index].substring(j,j+1).equals(a[y].substring(k,k+1))) {
						//如果某个字母不相同,表示两个字符串没有公共部分。无法连接在一起
						return 0;
					}
					
				}
				return a[y].length()-k-1;//能到这,最起码都是第一个匹配成功
			}
		}
		return 0;
	}
	public static void dfs(int x) {//x为当前位置
		for(int i=1;i<=n;i++) {
			if(use_times[i]<2&&find(x,i)>0) {//单词不能用两次,并且不能包含:表示连接
				use_times[i]++;//使用次数加一
				ans+=find(x,i);//加上新单词长度
				dfs(i);//接着这个单词搜索
				use_times[i]--;//回溯
				ans-=find(x,i);
				
			}
		}
		if(ans>p)
			p=ans;
		
	}
	  
	  
	
 public static void main(String[] args)  {
  Scanner cin =new Scanner(System.in);
   n=cin.nextInt();//单词数
   int maxn=0;
  for(int i=1;i<=n;i++) {//存储单词
	  a[i]=cin.next();
  }
  String b=cin.next();//龙的开头
  for(int i = 1; i <= n; i++)
  {
      if(a[i].substring(0,1).equals(b))//查找首字母为龙的开头的单词
      {	
          ans = a[i].length();//先加上首单词的长度 
          use_times[i]++;//首单词用过了 
          dfs(i);//以第i个单词为开头,尝试计算长度
          use_times[i]--;//回溯 
          if(p > maxn)//求最大 
          {
              maxn = p;
          }
      }
  }
  System.out.println(maxn);
  cin.close();
	  
 }
}

 --------------------------------------------------------------------------------------------------------------------------------

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/54910.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

java+mysql基于SSM共享型汽车租赁系统-计算机毕业设计

项目介绍 共享汽车租赁公司的共享汽车租赁流程复杂、数据庞大&#xff0c;往往一个疏忽就会给公司造成极大的损失&#xff0c;于是越来越多的共享汽车租赁公司需要一个对各项信息的管理平台来避免这样的损失。为了满足这个需求&#xff0c;我们开发一个针对共享汽车租赁信息的…

MR直播实例(混合现实直播)高品质企业直播

阿酷TONY / 2022-12-2 / 长沙 / 超多组图 绿幕抠像 虚拟场景&#xff08;三维场景&#xff09;实时渲染&#xff0c;降低直播成本&#xff0c;带来线下活动所没有的沉浸式视听体验。 虚拟舞台场景介绍参见&#xff1a; 企业年会直播来个虚拟舞台场景如何&#xff1f;_阿…

负载均衡与高可用

目录 负载均衡 理论部分 应用层负载均衡 环境搭建 代理服务器配置 web服务器配置 验证 网络层负载均衡 环境搭建 代理服务器配置 mysql服务器配置 验证 高可用 理论部分 环境搭建 负载均衡高可用 lb1主要服务器配置 lb2备份服务器配置 web配置 验证 nginx故障问题 →→→→ 大虾…

【MySQL】-增删查改

作者&#xff1a;学Java的冬瓜 博客主页&#xff1a;☀学Java的冬瓜&#x1f319; 专栏&#xff1a;MySQL 分享&#xff1a;至若春和景明&#xff0c;波澜不惊&#xff0c;上下天光&#xff0c;一碧万顷。沙鸥翔集&#xff0c;锦鳞游泳&#xff0c;岸芷汀兰&#xff0c;郁郁青青…

傻妞旧版合集新版订阅

目录一、前言二、下载三、新版傻妞订阅合集一、前言 傻妞旧版本(合集),包含amd和arm版本收集于TG 我的是amd架构 [rootecs-mike_note ~]# cat /proc/version Linux version 4.11.8-1.el7.elrepo.x86_64 (mockbuildBuild64F25) (gcc version 4.8.5 20150623 (Red Hat 4.8.5-11…

Vue中的计算属性

计算属性&#xff1a;实际上是把vm中的属性进行计算加工&#xff0c;最后能够返回给页面一个结果 细想一下&#xff0c;其实methods方法也能实现1中描述的现象&#xff0c;但是计算属性最大的优势是缓存&#xff01;&#xff01;&#xff01; 举个例子 <div id"root&q…

【Android App】物联网实战项目之自动驾驶的智能小车(附源码和演示 超详细)

需要源码请点赞关注收藏后评论区留言私信~~~~ 当今社会正在步入一个万物互联的时代&#xff0c;它的技术基石主要来自5G、物联网和人工智能。 三者融合产生了许多新产品&#xff0c;其中最璀璨的当数自动驾驶的电动车&#xff1b;它汇聚了最新科技与工程实践的成果&#xff0c;…

【收纳】电脑资料-高效整理电脑上的文件

以前没想过整理文件&#xff0c;一般都是建各种文件夹&#xff0c;然后把资料拖进去&#xff0c;好像收起来了&#xff0c;但实际是随着文件越来越多&#xff0c;时间一久&#xff0c;经常找不到文件&#xff1b;后来逐渐意识到&#xff0c;需要改变。于是找了很多方法&#xf…

已分区的硬盘如何重新合并, 分出去的盘怎么重新合并

已分区的硬盘如何重新合并&#xff1f;磁盘分区后合区是指对原磁盘的分区进行合并&#xff0c;使之成为一个的磁盘分区&#xff0c;从具体的应用层面来分析&#xff0c;为什么会对磁盘分区后合区呢&#xff1f; 磁盘管理对磁盘分区后合区 在Windows系统中&#xff0c;磁盘管理…

UGUI性能优化学习笔记(二)合批

一、合批规则 合批&#xff1a;把渲染时使用相同材质、相同贴图的网格合并在一起&#xff0c;成为一个大网格&#xff0c;然后再调用一次Draw Call&#xff0c;直接渲染这一个大网格。这样做可以降低Draw Call的数量&#xff0c;以优化性能。 Unity是如何确定哪些网格可以进行…

[附源码]JAVA毕业设计货币博物馆展品管理系统(系统+LW)

[附源码]JAVA毕业设计货币博物馆展品管理系统&#xff08;系统LW&#xff09; 目运行 环境项配置&#xff1a; Jdk1.8 Tomcat8.5 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项…

常用框架技术- 08 Spring Cloud简单易懂、易部署和易维护的分布式系统开发工具包

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录前言1.微服务理论1.1 微服务的特点1.2 微服务与微服务架构1.3 微服务的优点2.分布式集群概念2.1 分布式集群概念2.1.1 什么是分布式2.1.2 什么是集群2.1.3 分布式集群…

新品上线 Naive Admin Tenant 开箱即用多租户开发框架

Naive Admin Tenant 是一套企业级的权限管理开发平台&#xff0c;采用前后端分离模式&#xff0c;微服务版本前端框架支持多个选择&#xff0c;支持数据库&#xff1a;MySql、Oracle、SqlServer、PostgreSql 等&#xff0c;目前只支持 MySql&#xff0c;后面有客户需求会扩展支…

三维数字沙盘大数据人工智能模拟对抗推演系统开发教程第一课

三维电子沙盘大数据人工智能模拟对抗推演系统开发教程第一课 该数据库中只提供 成都市火车南站附近的数据请注意&#xff0c;104.0648,30.61658 而且该公用服务器带宽不大&#xff0c;所以会有些卡顿&#xff0c;建议下载数据库后本地使用&#xff0c;下载后的数据库有些许变…

《InnoDB引擎八》InnoDB关键特性-两次写

InnoDB 关键特性 InnoDB存储引擎的关键特性包括&#xff1a; Insert Buffer (插入缓冲)Double Write (两次写)Adaptive Hash Index (自适应哈希索引)Async IO (异步IO)Flush Neighbor Page (刷新领接页) 这些特性为InnoDB存储引擎带来了更好的性能以及更高的可靠性。 两次写 …

STM32 定时器单脉冲模式的使用记录

一、我要解决的问题 我的需求 需要单片机几乎同时在A,B,C 三个IO 分别输出T1&#xff0c;T2&#xff0c;T3 时长的高电平&#xff0c;时间结束后&#xff0c;恢复低电平。 初步思路 面对这个需求&#xff0c;我第一时间想到的是用三个定时器&#xff0c;分别设置T1,T2,T3 时…

Win10解决:系统管理员已阻止你运行此应用

前言 Win10安装msi软件包时出现错误提示如下&#xff1a; 解决 按【winR】快捷键打开运行&#xff0c;输入gpedit.msc回车依次进入”Windows设置“—”安全设置“—”本地策略“–”安全选项“—”用户账户控制&#xff1a;以管理员批准模式运行所有管理员“—双击&#xf…

JUC系列(六) 线程池

&#x1f4e3; &#x1f4e3; &#x1f4e3; &#x1f4e2;&#x1f4e2;&#x1f4e2; ☀️☀️你好啊&#xff01;小伙伴&#xff0c;我是小冷。是一个兴趣驱动自学练习两年半的的Java工程师。 &#x1f4d2; 一位十分喜欢将知识分享出来的Java博主⭐️⭐️⭐️&#xff0c;擅…

若依 数据权限图文详细理解及改造

数据权限 所谓数据权限&#xff0c;就是根据不同角色的登录用户&#xff0c;查看不同的列表数据 若依数据权限 若依的数据权限也是基于角色实现的&#xff0c;支持五种权限模式&#xff0c;按权限大小一次排列&#xff1a; 全部数据权限&#xff0c;表示拥有所有部门的数据…

远程办公:通过cpolar内网穿透,远程桌面控制家里公司内网电脑

疫情反反复复的当下&#xff0c;有时候会遇到需要居家办公的情况&#xff0c;但在办公室的电脑上仍有很多重要资料需要存取&#xff0c;且办公室所在的局域网中也有很多相关资源需要被访问&#xff08;如文件共享服务器、OA系统等&#xff09;。如何能在家通过远程处理好办公事…