竞赛题目讲解-【NOIP2000复赛 普及组】单词接龙

8783:单词接龙


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

Input
输入的第一行为一个单独的整数n(n<=20)表示单词数,以下n行每行有一个单词(只含有大写或小写字母,长度不超过20),输入的最后一行为一个单个字符,表示“龙”开头的字母。你可以假定以此字母开头的“龙”一定存在。
Output
只需输出以此字母开头的最长的“龙”的长度。

Sample Input

5
at
touch
cheat
choose
tact
a

Sample Output

23

Hint
连成的“龙”为atoucheatactactouchoose


题目讲解
这道题直接看十分复杂,“接龙”时的规则特别多,我们可以放在后面一个个来处理。
首先是变量的选择。它的输入是字符串,那么我们就有2种选择:字符串stringchar数组,两种方法都可以解出题目。但是,作者在这里推荐使用string,这是C++的一个新特性,因此关于它的函数也特别多,便于解题。虽说string的操作特别慢(这也是一些题不能用string的原因),但这道题的操作量并不是特别大,还不至于超时。
我们先读入n个单词(string用cin读入),然后读入开头字母。这里有一个误区——我们往往会默认为这道题的开头字母只会指向一个单词,但是出现相同字母开头的单词几率特别大,我们必须找到所有可以从它开始的单词。
顺着题意,我们需要进行接龙。既然这是一道搜索题,我们就可以选择深度优先搜索和广度优先搜索。这里由于作者用的是string,就不得不用深度优先搜索(为什么呢?string除了操作慢,它占用的内存更是多,若以string为队列基本类型很容易内存超限)。又由于深度优先搜索,我们有必要判断一下重复。根据之前的经验,作者第一次想到的是map(关联式容器)。就是这样:(不懂关联式容器的同学可以看看这道题:map专练-统计数字

map<string,bool> ma;

但是这个想法马上被否认了,因为作者发现题目中规定一个单词只能用2次,一个更简便的方法是用数组。由于我们是按顺序输入的,我们可以用它的编号代替它使用的次数
为了模拟接龙的过程,函数的形参为:string L。
接下来我们开始写函数。首先是一个保存答案的操作,我们需要更新答案的最大值。然后遍历所有的单词,若还可以使用(使用次数<2),则进入。注意此时不要判断是否能够前后相接,因为若两个串存在重合的一部分,则我们无法直接判断。当我们进入后,通过取子串的方式,可以判断是否能够相接。若可以,要注意不要直接相接,要将重合的部分排除掉再相接(相接可以直接用string的“+”)。最后别忘了回溯
回到main函数后,我们就进行输出,下次循环的时候,别忘了初始化答案和单词的使用次数


题外话
各位同学曾为流输入输出速度慢而烦恼吗?我们可以调用一个神奇的东西来实现流加速(我在书上看到的,也不知道为什么):

ios::sync_with_stdio(false);

也就是关闭了流输入输出和stdio的关联。其中的具体我还不知道,知道的朋友评论一下,谢谢!


程序样例
1次Accepted,感觉自己挺6的

/*Lucky_Glass*/
#include<iostream>
#include<cstring>
#include<iostream>
using namespace std;
string s[25];
int n,use[25],ans;
char ch;
void flag(string L)
{
    ans=max(ans,(int)L.size());
    for(int i=0;i<n;i++)
        if(use[i]<2)
        {
            bool Continue=true;
            int Size=L.size(),j;
            for(j=1;j<Size;j++)
            {
                string sub=L.substr(Size-j,j);
                char S1[1005],S2[25];
                strcpy(S1,sub.c_str());
                strcpy(S2,s[i].c_str());
                if(strstr(S2,S1)-S2==0)
                {
                    Continue=false;
                    break;
                }
            }
            if(Continue) continue;
            use[i]++;
            flag(L+s[i].substr(j,s[i].size()-j));
            use[i]--;
        }
}
int main()
{
    int Ans=0;
    ios::sync_with_stdio(false);
    cin>>n;
    for(int i=0;i<n;i++)
        cin>>s[i];
    cin>>ch;
    for(int i=0;i<n;i++)
        if(s[i][0]==ch)
        {
            ans=0;
            memset(use,false,sizeof(use));
            use[i]++;
            flag(s[i]);
            Ans=max(Ans,ans);
        }
    cout<<Ans<<endl;
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值