HDU 1671 Phone List(Trie)

来源:互联网 发布:大数据的优劣 编辑:程序博客网 时间:2024/06/03 03:22

题目链接

题意
      给定n个电话号码,看看是否存在某些号码是别的号码的前缀,若存在,输出“NO",否则输出”YES“。

数据范围的一些限定
1 <= n <= 10000
电话号码长度不超过10个数字

思路
      暴力的做法先输入完所有的号码,然后从第一个开始到最后一个,一个一个检测当前号码是否是其他号码的前缀,判断当前号码是否是别的号码的前缀的方法大家应该都懂,就是逐个数字对比直到对比完当前号码为止。
      看到这种一个字符串跟一堆字符串逐个进行比较的操作的时候,就会想起Trie。要使用Trie的话,必须解决一个问题,怎么判断一个字符串是Trie中的某个字符串的前缀。如果字符串tmp是Trie中某个字符串的前缀,那么tmp对应的单词结点必然会有子结点,可以依此来判断。
      再看一看题目,题目要求只要有一个号码是别的号码的前缀,就输出”NO“。那么就不用把所有输入的电话号码都插入Trie了,可以边输入边判断,把当前输入的号码插入Trie中并检查Trie中是否存在一个号码是其他Trie中任一号码的前缀,存在则以后的输入不再插入Trie中,要输出什么就很容易判断了。
      边输入边判断的方法有一个要注意的地方。向Trie插入号码的时候要注意检查两件事。向Trie插入号码b时,要检查路径上已存在的结点中是否有单词结点,若有,则Trie中必然存在一个号码是号码b的前缀,反之则当前Trie中任何的非b号码都不是b的前缀。插入b的单词结点时还要检查该单词结点是否已被创建过(创建过是指有单词路过这个点,即child数组相应的值不为0),若该结点已被创建过,则b必然是Trie中某个(至少一个,有多少个不能从子结点的个数来判断,发散一下,与题目无关)号码的前缀,反之Trie中任何的非b号码都不以b为前缀。
      更具体一些的细节请自己看下面附上的代码吧。

      以下是代码,各种多余头文件宏定义什么的请无视,在vimrc里写好了,懒得删掉了,可以视为我在装13。如果在其中发现有错误,请指出,发表此文部分原因是希望有人能够指出我可能存在的错误。如果有人有更好的解法,请不吝赐教。

/* * Author:  Fiend * Created Time:  2013/4/8 15:36:16 * File Name: test.cpp */#include <iostream>#include <cstdio>#include <cstddef>#include <cmath>#include <algorithm>#include <string>#include <cstring>#include <vector>#include <bitset>#include <stack>#include <queue>#include <set>#include <map>#include <cctype>#define ST size_type#define PB push_back#define LL long long#define SIGMA_SIZE 15#define NODE_SIZE 100005using std::cin;using std::cout;using std::endl;using std::string;using std::bitset;using std::vector;using std::pair;using std::swap;using std::sort;using std::max;using std::min;const int inf = 0x3fffffff;typedef pair<int, int> pii;typedef vector<int> vi;typedef vector<int>::iterator vit;class Trie {    private:        int child[NODE_SIZE][SIGMA_SIZE];        bool val[NODE_SIZE];//记录该结点是否为单词结点        int cnt;    //结点总数    public:        //初始时只有一个根结点        Trie () {            cnt = 1;            memset (child[0], 0, sizeof (child[0]));        }        //将trie重置为只有一个根结点        void clear () {            cnt = 1;            memset (child[0], 0, sizeof (child[0]));        }        //返回字符ch的编号//根据字符集的不同进行调整        int getIndex (char ch) {            return ch -'0';        }        /*         * 插入字符串src         * src是string类型,插入前不用查询是否存在         * 对象做实参要加引用,不然每次一调用函数就要复制一个对象,很影响效率 * 若src在Trie中存在其前缀或者Trie中存在字符串以src为前缀,则返回true,否则返回false         */        bool insert (string &src) {            int parent = 0, index;bool rtn = false;            for (string::size_type i = 0; i != src.size(); ++i) {                index = getIndex (src[i]);                if (child[parent][index] == 0) {    //结点不存在                    child[parent][index] = cnt;     //新建结点                    memset (child[cnt], 0, sizeof(child[cnt]));                    val[cnt] = false;//非单词结点                    ++cnt;                } else {//该结点已被创建过//插入号码时的两项检查if (i + 1 == src.size() || val[child[parent][index]])return true;}                parent = child[parent][index];  //往下走            }            val[parent] = true;//单词结点return rtn;        }};Trie t;int main () {int T, n;string tmp;bool ans;cin >> T;while (T--) {t.clear ();ans = false;cin >> n;for (int i = 1; i <= n; ++i) {cin >> tmp;if (ans == false)ans = t.insert(tmp);}if (ans)cout << "NO" << endl;elsecout << "YES" << endl;}return 0;}



原创粉丝点击