洛谷P1019(dfs+字符串处理)

8 篇文章 0 订阅
3 篇文章 0 订阅

题目描述

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

输入输出格式

输入格式:

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

输出格式:

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


重点:单词重叠的部分不好操作

在搜索的基础上代码需要有所修改。

首先,题目包含的接龙要求大致有以下几点:1.同一个单词最多出现2次 2.相邻的两部分不能存在包含关系 3.在两个单词相连时,其重合部分合为一部分。

我们很容易就可以想到,使用一个结构体用于存储各个单词的数据,包括单词本身,它的实际长度以及可供使用的剩余次数(初始值为2).如下:

struct words
    {
        int left = 2, tail = 0;            //剩余的使用次数 单词实际长度
        char word[101];                    //内容
    }words[22];
我们可以使用一个字符变量start用来存储开头的字母,然后即可开始搜索,由于start当中的字符并不会被计入总长度,所以可以先通过一个循环,找到符合题意的单词,然后再以这个单词作为开头,进行深搜。
 cin >> start;
        for (int i = 0; i < wordTot; i++)
            if (words[i].word[0] == start)
            {
                words[i].left--;            
                dfs(words[i].word, words[i].tail);        //目前龙中最后一个单词 长度
                words[i].left++;
            }

由于单词接龙中相邻的两部分不能存在包含关系,所以无论龙有多长,只需要考虑最后一个加入的单词能够和那些单词拼接即可。在这里,我们可以编写一个函数juj来判断两个单词之间重合部分长度的最小值。
int juj(char a[], char b[])
    {
        int al = strlen(a), bl = strlen(b), j;
        bool flag = true;
        for (int i = 1; i < al && i < bl; i++)
        {
            flag = true;
            for (j = 0; j < i; j++)
                if (a[al - i + j] != b[j]) { flag = false; break; }
            if (flag)
                return i;
        }
        return 0;
    }

这个函数主要的原理是:在a,b两个字符串当中一个个地尝试可能重合部分的长度,并把这个长度作为一个数值返回。如果这个长度已经不小于当中最短字符串的长度,那么说明这两个字符串没有符合题意的重合部分,juj将返回0。

处理好了单词重合部分,剩下的就是搜索了。这部分比较简单,直接照着深搜的伪代码模板进行填充相应的代码即可。

void dfs(char tail[], int num)
    {
        _max = max(_max, num);        //更新最大值
        int a;                        //两个单词重合部分的长度
        for (int i = 0; i < wordTot; i++)
            if (words[i].left > 0)
            {
                a = juj(tail, words[i].word);
                if (a != 0)            //判断是否符合题意
                {
                    words[i].left--;
                    dfs(words[i].word, num + words[i].tail - a);        //下一步搜索
                    words[i].left++;
                }
            }
    }

这样,程序就基本完成了,下面是完整的代码:
#include<iostream>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    int wordTot, _max = 0;
    struct words
    {
        int left = 2, tail = 0;            //剩余的使用次数 单词实际长度
        char word[101];                    //内容
    }words[22];
    int juj(char a[], char b[])
    {
        int al = strlen(a), bl = strlen(b), j;
        bool flag = true;
        for (int i = 1; i < al && i < bl; i++)
        {
            flag = true;
            for (j = 0; j < i; j++)
                if (a[al - i + j] != b[j]) { flag = false; break; }
            if (flag)
                return i;
        }
        return 0;
    }
    void dfs(char tail[], int num)
    {
        _max = max(_max, num);        //更新最大值
        int a;                        //两个单词重合部分的长度
        for (int i = 0; i < wordTot; i++)
            if (words[i].left > 0)
            {
                a = juj(tail, words[i].word);
                if (a != 0)            //判断是否符合题意
                {
                    words[i].left--;
                    dfs(words[i].word, num + words[i].tail - a);        //下一步搜索
                    words[i].left++;
                }
            }
    }
    int main()
    {
        char start;
        cin >> wordTot;
        for (int i = 0; i < wordTot; i++)
        {
            cin >> words[i].word;
            words[i].tail = strlen(words[i].word);
        }
        cin >> start;
        for (int i = 0; i < wordTot; i++)
            if (words[i].word[0] == start)
            {
                words[i].left--;
                dfs(words[i].word, words[i].tail);        //目前龙中最后一个单词 长度
                words[i].left++;
            }
        cout << _max << endl;
        return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值