学习字典树有段日子了!上次去做福州赛区2010年的试题的时候,里面有一道题:字符串的多串匹配!当时就觉得应该是字典树的题,但是分析了一下后数据量太大了!就没做它!下来翻书一看,果然跟字典树有关,但不是字典树!如果用字典树肯定会超时,而不是超空间!
不管怎么样,还是基础的数据结构和算法都没有学全,没有进行系统的学习啊!很多时候感叹,如果有老师能指点一下,如果有志同道合的朋友一起学习算法,也许结果就不是现在这个样子了!无论怎么样,坚持着往前走吧!
字典树就很简单了,网上有很多很多的说明和代码,其思想也很容易理解:就是把字符串集合中前缀相同的部分用同一个结点表示! 回想起前两天没有研究透彻的KMP算法,以及后缀数组。发现字符串的问题,不是用到了前缀,就是用到了后缀!
贴一张原理图,然后给出我个人觉得相当优雅的实现代码吧!
- /*
- http://poj.org/problem?id=3630
- POJ 3630 Phone List
- 大意:电话簿中有n个电话号码,判断这些号码是否合法。
- 若某个电话号码是另一个电话号码的前缀,则该号码簿非法
- 分析:字典树即可
- 注意点,字典树在插入过程中新建节点会超时,故节点用数组的方式存储
- */
- #include<stdio.h>
- #include<string.h>
- #define N 10
- #define M 100000
- struct TrieNode{
- TrieNode *next[N];//由于这里面实际上已经包含了data,所以data是不需要的
- bool isTail;
- TrieNode(){
- isTail=false;
- memset(next,NULL,sizeof(next));
- }
- };
- TrieNode node[M];//这个里面存储了所有的结点
- class Trie{
- public:
- Trie();
- bool insert(char *word);
- void clear();
- private:
- TrieNode *root;
- int nodeNum;//树中实际出现的节点数
- };
- Trie::Trie(){
- root=&node[0];
- nodeNum = 1;
- }
- bool Trie::insert(char *word){
- TrieNode *p=root;
- while(*word){
- int current=*word-'0';
- if(p->next[current]==NULL){
- p->next[current]=&node[nodeNum++];
- }
- p=p->next[current];
- if(p->isTail==true){
- return true;
- }
- word++;
- }
- p->isTail=true;
- //如果它的后面还有结点,则表明此串必为一串的子串
- for(int i=0;i<N;i++){
- if(p->next[i]!=NULL){
- return true;
- }
- }
- return false;
- }
- void Trie::clear(){
- for(int i=0;i<nodeNum;i++){
- memset(node[i].next,NULL,sizeof(node[i].next));
- node[i].isTail=false;
- }
- nodeNum=1;//这一步是容易忘记的
- }
- int main(){
- int T;
- char ch[M];
- scanf("%d",&T);
- Trie trie;
- while(T-->0){
- int n;
- scanf("%d",&n);
- trie.clear();
- bool isIll=false;
- while(n-->0){
- scanf("%s",ch);
- if(isIll==true)
- continue;
- isIll=trie.insert(ch);
- }
- if(isIll){
- printf("NO\n");
- }else{
- printf("YES\n");
- }
- }
- return 0;
- }
字典树就介绍完了!可是遇到新问题的时候,比如:HDU2222,虽然题目是英文的,挺容易看懂!字典树就用不上了!那需要用什么呢?其实也有很多算法,我也没学习过,比如:AC自动机,WM算法等等!主要是KMP都没过关,那些算法也学习不了啊!但是Trie树学习过,而Trie图是可以解决这类问题的!果断学习一下Trie图吧!
对于hdu2222,先把超时的代码贴上来吧!记得马云好像说过,多去学习失败的经验!
- /*
- * Trie图:解决多串匹配问题
- * hdu2222
- * */
- #include<stdio.h>
- #include<string.h>
- #define M 1000005
- #define N 100000
- char word[60];
- char s[M];
- struct Node{
- bool tail;
- Node *next[26];
- Node(){
- memset(next,0,sizeof(next));
- tail=false;
- }
- }tree[N],*root;
- int nodeNum;
- void init(){
- root=&tree[0];
- nodeNum=1;
- }
- void insert(char *word){
- Node *p=root;
- int idx;
- while(*word){
- idx=*word-'a';
- if(p->next[idx]==0){
- p->next[idx]=&tree[nodeNum++];
- }
- p=p->next[idx];
- word++;
- }
- p->tail=true;
- }
- void clear(){
- int i;
- for(i=1;i<nodeNum;i++){
- memset(tree[i].next,0,sizeof(tree[i].next));
- tree[i].tail=false;
- }
- nodeNum=1;
- }
- int search(char *s){
- int ans=0;
- Node *p;
- int i,idx,cur,len;
- len=strlen(s);
- for(i=0;i<len;i++){
- idx=i;p=root;
- while(1){
- cur=s[idx]-'a';
- if(p->tail==true)ans++;
- if(p->next[cur]==0)break;
- p=p->next[cur];idx++;
- }
- }
- return ans;
- }
- int main(){
- int t,n,i;
- scanf("%d",&t);
- init();
- while(t--){
- clear();
- scanf("%d",&n);
- for(i=0;i<n;i++){
- scanf("%s",word);
- insert(word);
- }
- scanf("%s",s);
- printf("%d\n",search(s));
- }
- }
好了,现在来看Trie图是怎么解决吧!Trie图很好懂,就是在Trie树的基础上把每个结点上增加一个前缀指针!至于Trie图的具体细节后面再学习,先把AC了的代码贴上来吧!
- /*
- * Trie图:解决多串匹配问题
- * hdu2222
- * */
- #include<stdio.h>
- #include<string.h>
- #include<queue>
- using namespace std;
- #define M 1000005
- char word[60];
- char s[M];
- struct Node{
- int tail;
- Node *prefix;
- Node *next[26];
- Node(){
- memset(next,0,sizeof(next));
- tail=0;
- }
- }*root;
- void init(){
- root=new Node();
- }
- void insert(char *word){
- Node *p=root;
- int idx;
- while(*word){
- idx=*word-'a';
- if(p->next[idx]==0){
- p->next[idx]=new Node();
- }
- p=p->next[idx];
- word++;
- }
- p->tail++;
- }
- void add_prefix(){
- root->prefix = NULL;
- deque<Node* > q;
- q.push_back(root);
- while(!q.empty()) {
- Node* tmp = q.front();
- Node* p = NULL;
- q.pop_front();
- for(int i = 0; i < 26; ++i) {
- if(tmp->next[i] != NULL) {
- if(tmp == root) tmp->next[i]->prefix = root;
- else {
- p = tmp->prefix;
- while(p != NULL) {
- if(p->next[i] != NULL) {
- tmp->next[i]->prefix = p->next[i];
- break;
- }
- p = p->prefix;
- }
- if(p == NULL) tmp->next[i]->prefix = root;
- }
- q.push_back(tmp->next[i]);
- }
- }
- }
- }
- int search(char *st){
- int cnt = 0, t;
- Node* p = root;
- while(*st) {
- t = *st - 'a';
- while(p->next[t] == NULL && p != root) {
- p = p->prefix;
- }
- p = p->next[t];
- if(p == NULL) p = root;
- Node* tmp = p;
- while(tmp != root && tmp->tail != -1) {
- cnt += tmp->tail;
- tmp->tail = -1;
- tmp = tmp->prefix;
- }
- st++;
- }
- return cnt;
- }
- int main(){
- int t,n,i;
- scanf("%d",&t);
- while(t--){
- init();
- scanf("%d",&n);
- for(i=0;i<n;i++){
- scanf("%s",word);
- insert(word);
- }
- add_prefix();
- scanf("%s",s);
- printf("%d\n",search(s));
- }
- }
由于时间的关系,暂时先把Trie图的学习放到一边,把参考博客列出来,供自己以后学习吧
http://baidutech.blog.51cto.com/4114344/743727
http://www.cnblogs.com/vongang/archive/2012/07/24/2606494.html