Hat’s Words (HDU - 1247)

题目描述

  A hat’s word is a word in the dictionary that is the concatenation of exactly two other words in the dictionary. 
You are to find all the hat’s words in a dictionary. 

Input

  Standard input consists of a number of lowercase words, one per line, in alphabetical order. There will be no more than 50,000 words. 
  Only one case. 

Output

  Your output should contain all the hat’s words, one per line, in alphabetical order.

Sample Input

a
ahat
hat
hatword
hziee
word

Sample Output

ahat
hatword

题目分析

  这次问题研究的是找出一种特别的字符串。题目很短,就一点点

Hat’s Words (HDU - 1247)_第1张图片

  这种字符串的特点就是,将它分成连续的两部分,比如panda这个字符串,可以这样分:p和ande;也可以分成pa和nda,一共有4种分法。分了之后,就判断它的这两个部分是不是读入的其他字符串。比如读入pan、panda、da, 那么panda可以分成pan和pa并且,pan和pa都可以在这三个字符串里找到原型,所以就说panda是这种特殊的字符串。题目将这种特殊字符串取样名为hat's word,帽子字符串。

  每次就输入一组测试,且均为小写字符,每行一个字符串,且按字典序给出,总共不超过50000个字符。

解题思路

  很无奈,也很明显,就是把每个字符串遍历拆开,然后观察拆分后的两部分,是否跟其他字符串匹配。如果都匹配成功就输出当前整个字符串。所以很搭字典树的搜索函数嘛,找到了就返回标志数(>=1即可),没找到就返回0。可能要注意的地方就是,拆分函数,因为没找到直接可以拆分的函数,所以就要自己编写,定义一个变量mid,用来判断从哪分开。小心前半部分和后半部分的mid,从代码看吧。(字典树理解请看字典树初步理解)

  对了,有一个更无奈的点,就是他那一点点的题目并没有给出每行字符串最长的上限,所以一开始我设置为50000,结果被反馈一个“what do you want to do?”,后来观察其他人AC的代码,发现将字符串长度开到20就够了,可能题短情长吧。

Hat’s Words (HDU - 1247)_第2张图片

AC代码

#include 
#include 
#include 
#include 
#include 
using namespace std;

#define ALPHABET_SIZE 26
char keys[50100][20];

typedef struct trie_node
{
    int count;   // 记录该节点代表的单词的个数
    trie_node *children[ALPHABET_SIZE]; // 各个子节点
}*trie;

trie_node* create_trie_node()
{
    trie_node* pNode = new trie_node();
    pNode->count = 0;
    for(int i=0; ichildren[i] = NULL;
    return pNode;
}

void trie_insert(trie root,char* key)
{
    trie_node* node = root;
    const char* p = key;
    while(*p)
    {
        if(node->children[*p-'a'] == NULL)
        {
            node->children[*p-'a'] = create_trie_node();
        }
        node = node->children[*p-'a'];
        ++p;
    }
    node->count += 1;
}

/**
 * 查询:不存在返回0,存在返回出现的次数
 */
int trie_search(trie root,char* key)
{
    trie_node* node = root;
    const char* p = key;
    while(*p && node!=NULL)
    {
        node = node->children[*p-'a'];
        ++p;
    }
    if(node == NULL)
        return 0;
    else
        return node->count;
}

int deltree(trie T)    //销毁字典树
{
    int i;
    if(T==NULL)
        return 0;
    for(i=0;i<10;i++)
    {
        if(T->children[i]!=NULL)
            deltree(T->children[i]);
    }
    free(T);
    return 0;
}

void divide(char* a, char* b, const char* c, int len, int mid){    //拆分函数
    //len为原字符串的长度,mid为拆分点,a存前部分,b存后部分,c为原字符串
    for(int i = 0; i < mid; i++)    //前半部分,从0到mid
        a[i] = c[i];    //由a数组存放
    a[mid] = '\0';    //记住最后要将'\0'赋值,字符串特性
    for(int i = 0; i <= len-mid; i++)    //这里就要注意了,虽然从原字符串看是从mid到len,但对于b数组是从0到len-mid,所以c要写成[i+mid],不然直接mid到len会不好判断。
        b[i] = c[i+mid];
}

int main()
{
    trie root = create_trie_node();    //直接建立根源结点,即建新树
    int k = 0;    //字符串数量变量
    while(~scanf("%s",keys[k])){    //因为题目没有给退出标志,所以只能凭借EOF作为退出标志
        trie_insert(root, keys[k++]);    //读入一个,插入一个,k数量再加1
    }
    for(int i = 0; i < k; i++){    //遍历所有k个字符串
        for(int j = 1; j < strlen(keys[i]); j++){
            char a[50],b[50];    //新建a,b字符串
            divide(a, b, keys[i], (int)strlen(keys[i]), j); //拆分
            int an1 = (trie_search(root,a));    //搜索匹配a
            int an2 = (trie_search(root,b));    //搜索匹配b
            if(an1 >= 1 && an2 >= 1){    //如果都匹配成功,就输出原字符串,并退出
                printf("%s\n",keys[i]);
                break;
            }
        }
    }
    deltree(root);    //虽然只有一棵树,但也该节省空间,佛系嘛
    return 0;
}

 

你可能感兴趣的:(字典树)