1. 程式人生 > >洛谷P1019(dfs+字串處理)

洛谷P1019(dfs+字串處理)

題目描述

單詞接龍是一個與我們經常玩的成語接龍相類似的遊戲,現在我們已知一組單詞,且給定一個開頭的字母,要求出以這個字母開頭的最長的“龍”(每個單詞都最多在“龍”中出現兩次),在兩個單詞相連時,其重合部分合為一部分,例如 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;
}