【模板】trie 树,第二题可作模板

上一次看到数据结构,忙着我的图论,匆匆几眼就看过去了。
这一次终于可以好好学习一番了;
网上资料有很多, 给我的感觉就是,建立一棵树,然后将26或者52 个字母,分成26叉树, 由此我们可以让很多字符串公用同一个前缀, 可以处理很多统计,啥啥的问题,以后做题慢慢做,慢慢学;
Ignatius最近遇到一个难题,老师交给他很多单词(只有小写字母组成,不会有重复的单词出现),现在老师要他统计出以某个字符串为前缀的单词数量(单词本身也是自己的前缀).

hdu 1251
用指针写的 Tire树,貌似有很多缺陷的(至少内存问题就存在),建立别当模板

Input
输入数据的第一部分是一张单词表,每行一个单词,单词的长度不超过10,它们代表的是老师交给Ignatius统计的单词,一个空行代表单词表的结束.第二部分是一连串的提问,每行一个提问,每个提问都是一个字符串.

注意:本题只有一组测试数据,处理到文件结束.

Output
对于每个提问,给出以该字符串为前缀的单词的数量.

Sample Input

banana
band
bee
absolute
acm

ba
b
band
abc

Sample Output

2
3
1
0

这个题就是要我们求下面的字符串,可以作为上面多少个字符串的前缀:

#include<iostream>
#include<stdio.h>
#include<string.h>
#include<math.h>
#include<algorithm>
#include<stdlib.h>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<vector>
const double PI = acos(-1.0);
const double e = exp(1.0);
template<class T> T gcd(T a, T b) { return b ? gcd(b, a % b) : a; }
template<class T> T lcm(T a, T b) { return a / gcd(a, b) * b; }
using namespace std;
typedef struct Tire{
    Tire *next[26];  //大写+小写=52    即x叉树
    int v; //可以用来统计个数
};
Tire root;

void creatTire(char *str){
    int len=strlen(str);
    Tire *p=&root,*q;
    for(int i=0;i<len;i++){
        int id = str[i]-'a'; // 从0开始存
        if(p->next[id] == NULL){
            q=(Tire *)malloc(sizeof(root));
            q->v = 1; //初识也可以不为1
            for(int j=0;j<26;++j)
                q->next[j] =NULL;
            p->next[id] =q;
            p=p->next[id];
        }
        else{
            p->next[id]->v++;
            p = p->next[id];
        }
    }
//    p->v = -1; //若为结尾,则v改成-1表示到时候查找的时候就知道结尾的地方
}
int findTire(char *str){
    int len=strlen(str);
    Tire *p=&root;
    for(int i=0;i<len;i++){
        int id=str[i]-'a';
        p= p->next[id];
        if(p==NULL){
            return 0; // 不存在这个str
        }
//        if(p->v==-1)
//            return -1; // 存在结果,且查找串正好在某个结尾处
    }
    return p->v; //查找串是字符串中的前缀
}
//在某些题目里面不是放空间  是会超内存的
int dealTire(Tire *t){
    int i;
    if(t=NULL)
        return 0;
    for(int i=0;i<26;i++){
        if(t->next[i]!=NULL){
            dealTire(t->next[i]);
        }
    }
    free(t);
    return 0;
}
char str[15];
int main(){
    //freopen("1.txt","r",stdin);
    int n;
    for(int i=0;i<26;i++)
        root.next[i]=NULL;
//    printf("HAH\n");
    while(gets(str) && str[0]!='\0'){
//        printf("%d\n",strlen(str));
        creatTire(str);
    }
    memset(str,0,sizeof(str));
    while(~scanf("%s",str)){
//        printf("%s\n",str);
        int ans=findTire(str);
        printf("%d\n",ans);
    }

  return 0;
}

感觉数据结构这种东西 没有为什么,理解了就是理解了,就是这个样子,没有为什么的;
下面来一道又有意思一点的题:
hdu 2846
2009 Multi-University Training Contest 4 - Host by HDU

When you go shopping, you can search in repository for avalible merchandises by the computers and internet. First you give the search system a name about something, then the system responds with the results. Now you are given a lot merchandise names in repository and some queries, and required to simulate the process.

Input

There is only one case. First there is an integer P (1<=P<=10000)representing the number of the merchanidse names in the repository. The next P lines each contain a string (it’s length isn’t beyond 20,and all the letters are lowercase).Then there is an integer Q(1<=Q<=100000) representing the number of the queries. The next Q lines each contains a string(the same limitation as foregoing descriptions) as the searching condition.

Output

For each query, you just output the number of the merchandises, whose names contain the search string as their substrings.

Sample Input

20
ad
ae
af
ag
ah
ai
aj
ak
al
ads
add
ade
adf
adg
adh
adi
adj
adk
adl
aes
5
b
a
d
ad
s

Sample Output

0
20
11
11
2

题意就是说:下面的字符串在上面多少个字符串中出现过, 如果数据小 的话,显然可以strstr什么的,但是这个数据范围导致你无法去用暴力做。
思路: 一开始正常对每一个字符串建树,发现了一个问题:
比如说: abc 建完树 之后 查找 b,正常的查找无法找到b,因为root->next[b]=NULL ,就直接结束了。如果你想在树上遍历的话也必然是会超时的。。。。

那怎么办呢? 既然查找我们无法修改,那我们只能在建树的过程中下功夫了。 我们对于一个字符串 abcd;
可以先用abcd建树,然后用bcd建树,…cd….d,都分别建树然后分别更新每个节点的次数什么的,最后就可以 普通查找实现了。 我们用这种方法就可以有root 直接找到b,c 等等。
如果用上一题的代码完成会发现会超出内存,于是又学习了一种用数组写的方法,异曲同工:

#include<iostream>
#include<stdio.h>
#include<string.h>
#include<math.h>
#include<algorithm>
#include<stdlib.h>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<vector>
const double PI = acos(-1.0);
const double e = exp(1.0);
template<class T> T gcd(T a, T b) { return b ? gcd(b, a % b) : a; }
template<class T> T lcm(T a, T b) { return a / gcd(a, b) * b; }
using namespace std;
typedef struct Tire{
    int v; //可以用来统计个数
    int id;
    int next[26];
};
int ptr=0;
Tire word[1000005];
/* 虽然知道节省空间,但并不知道为啥
 * */
void init(int z){
    for(int i=0;i<26;i++)
        word[z].next[i]=-1,word[z].v=0;  //初始化为-1
}
void creatTire(char *str,int x){
    int len=strlen(str);
    int cnt=0;
    for(int i=0;i<len;i++){
         if(word[cnt].next[str[i]-'a']==-1){
             word[cnt].next[str[i]-'a']=++ptr;
             init(ptr);   // 相当于初始化新开的节点
             cnt=ptr;     // 相当于移位
         }
         else{
             cnt=word[cnt].next[str[i]-'a'];
//             word[cnt].v++   下面有进行++;
         }
         if(word[cnt].id!=x) {
             word[cnt].v++;
         }
            word[cnt].id=x;
    }
}
int findTire(char *str){
    int len=strlen(str);
    int cnt=0;
    for(int  i=0;i<len;i++){
        int id=str[i]-'a';
        if(word[cnt].next[id]==-1){
            return 0; // 不存在这个str
        }
        else
            cnt=word[cnt].next[id];
    }
    return word[cnt].v; //查找串是字符串中的前缀
}

char str[30];
char ss[30];
int main(){
   // freopen("1.txt","r",stdin);
    int n;
    init(0);  //  又出现一个特容易忘的init了
    scanf("%d",&n);
    int cnt=1;
    while(n--){
        scanf("%s",str);
        int len=strlen(str);
        for(int i=0;i<len;i++)
            creatTire(&str[i],cnt);
        cnt++;
    }
//    int cnt;
    scanf("%d",&cnt);
    while(cnt--){
        scanf("%s",ss);
        printf("%d\n",findTire(ss));
    }
  return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值