单词接龙

原题链接:https://www.luogu.org/problem/show?pid=1019

基本思路是搜索。

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

单词的使用次数很好判断,开一个数组即可,和正常向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函数很像,具体到代码上大家就能看明白了。

 1 #include <iostream>
 2 #include <cstdio>
 3 #include <cstring>
 4 #include <string>
 5 #define maxn 100
 6 using namespace std;
 7 int n;
 8 int ans = 0;
 9 string word[maxn];//字符串数组,用来存储单词
10 string beginn;//用来存储开头字符
11 int used[maxn];//这个就是用来记录dfs时候每一个单词被使用了几次的数组
12 
13 bool check(string s,string m,int k){//重点一,check函数判断接口可行性,k代表接口长度,以下同
14     int lens = s.length();
15     for (int i=0;i<k;i++){
16         if(s[lens-k+i]!=m[i])//我讲过了
17             return false;
18     }
19     return true;
20 }
21 
22 void add(string &s,string m,int k){//拼接操作,因为要把m接到s上,所以对于s串不可以传参,因为我们要试图改变这个串
23     int lenm = m.length();
24     for (int i=k;i<lenm;i++)
25         s+=m[i];//C++字符串类型特性操作,支持+=符号直接拼接
26 }
27 
28 void dfs(string now){//这只是一个看似平淡无奇的dfs
29     int x = now.length();
30     ans = max(ans,x);//每次拼接之后更新一下答案
31     for (int i=1;i<=n;i++){
32         if (used[i]>=2)//如果有一个单词用完了,那这个单词就不能选了
33             continue;
34         int maxk = word[i].length();
35         for (int j=1;j<=maxk;j++){//枚举接口长度
36             if (check(now,word[i],j)){
37                 string temp = now;//重点二,使用字符串副本进行拼接
38                 add(temp,word[i],j);
39                 if (temp==now)//拼完之后如果发现长度没增加,也就是和原串一样,那这次拼接没有意义,剪掉
40                     continue;
41                 used[i]++;
42                 dfs(temp);
43                 used[i]--;//这只是一个看似平淡无奇的回溯
44             }
45         }
46     }
47     
48 }
49 
50 int main(){
51     cin >> n;
52     for (int i=1;i<=n;i++)
53         cin >> word[i];
54     cin >> beginn;
55     dfs(beginn);
56     
57     cout << ans << endl;
58     return 0;
59     
60 }

 

转载于:https://www.cnblogs.com/OIerShawnZhou/p/7482604.html

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值