字典树模板

struct Trie  //字典树结构
{
    Trie *child[26];
    int num;   //子节点数
    bool end;  //判断该字母是否为某个单词的结束字母
    Trie() //构造函数
    {
        num=0;
        end=0;
        memset(child,0,sizeof(child));
    }
};
Trie *root,*s,*lrelia;
void Create(char *str)  //插入单词
{
    s=root;
    int i=0;
    while(str[i])
    {
        int id=str[i]-'a';
        if(s->child[id]==0) //如果该字母还没有出现在字典中
        {
        s->child[id]=new Trie;
        s->num++; //子节点数+1
        s=s->child[id];
        }
        else
        {
            s=s->child[id];
        }
        i++;
    }
    s->end=1;//标记单词的最后一个字母
}
double Search(char *str)
{
    s=root;
    double len=1; //因为第一个字母必须得敲击,所以len赋值1,
    int o=strlen(str);
    /*
     既然前一个字母的子节点数决定下一个字母是否要敲击,
     我们只需要遍历第1个到len-1个字母就可以了。
    */
    for(int i=0;i<o-1;++i)
    {
        int id=str[i]-'a';
        s=s->child[id];
        if(s->num>1||s->end==1) len++;
    }
    return len;
}

 上面的模板是动态的,优点:节约空间,代码简洁,缺点:太慢了,比如POJ3630 Phone list,动态模板和静态模板相差了10倍!

静态模板:

#define mod 20071027
#define MAXN 400005
int dp[5555],num,re,p;
char a[300005];
struct Trie
{
    int child[26];
    bool end;
    Trie()
    {
      end=0;
      memset(child,0,sizeof(child));
    }
    void set()
    {
        end=0;
        memset(child,0,sizeof(child));
    }
}t[MAXN];
void Create(char *s)
{
    int root=0,i=0,id;
    while(s[i])
    {
        id=s[i]-'a';
        if(t[root].child[id]==0)
            t[root].child[id]=p++;
        root=t[root].child[id];
        i++;
    }
    t[root].end=1;
}
void Search(char *s)
{
    int root=0,i=0,id,len=strlen(s);
    for(i=0;i<len;++i)
    {
        id=s[i]-'a';
        if(t[root].child[id])
        {
            root=t[root].child[id];
            if(t[root].end)
            {
                num+=dp[re+i+1];
            }
        }
        else break;
    }
}

 更高效的静态模板:

#define mod 20071027
#define MAXN 300005
int dp[MAXN];
char a[MAXN];
struct Trie
{
    int ch[MAXN][26];
    bool val[MAXN];
    int sz;
    void clear()
    {
        sz=1;
        memset(ch[0],0,sizeof(ch[0]));
    }
    void Create(char *s)
    {
        int u=0,n=strlen(s); //u相当于根节点
        for(int i=0;i<n;++i)
        {
            int c=s[i]-'a';
            if(!ch[u][c])
            {
                memset(ch[sz],0,sizeof(ch[sz]));
                val[sz]=0;
                ch[u][c]=sz++;
            }
            u=ch[u][c];
        }
        val[u]=1;
    }
    void Search(char *s,int pos)
    {
        int i=0,u=0;
        while(s[i]!=0&&ch[u][s[i]-'a'])
        {
            u=ch[u][s[i]-'a'];
            if(val[u])
            {
                dp[pos]=dp[pos+i+1]?dp[pos]+dp[pos+i+1]:dp[pos];
                if(dp[pos]>=mod)
                    dp[pos]-=mod;
            }
            ++i;

        }
    }
}tr;

 o(︶︿︶)o GO DIE,居然还有前向星式的模板,一维的

前向星式图解:

/*
题意: 给出n个字符串, 计算两两比较的次数. 每次比较都需要比较(str1[i] == str2[i])和 (str1[i] == '\0'各一次).
点评:
将N个字符串插入前缀树,‘\0’也插入,这样就能区分字符串的终结点
S1与S2的共同前缀S,则比较次数为len(S)*2+1
但S1与S2相同,则比较次数为 (len(S1)+1)*2 (注意:这时连'\0’都算进去了噢~,因为适用性,所以模板最好是用一维的前向星式的
*/
#include <cstdio>
#include <iostream>
#include <cstring>
using namespace std;
#define MAX 1005
const int maxnode = 4001*1000+5;
const int sigma_size = 26;
typedef long long ll;
struct Trie
{
    int first[maxnode], next[maxnode]; //前向星式子建图还记得么,亲?
    int total[maxnode], ch[maxnode]; //total[i]记录每个节点被遍历的的次数,ch[i]记录字符串的值的
    int sz;  //记录ch的坐标的
    void clear()
    {
        sz = 1;
        total[0] = first[0] = next[0] = 0;
    }
   
    void insert(const char *s)
    {
        int u = 0, v, n = strlen(s);
        total[u]++;
        for(int i = 0; i <= n; ++i)
        {
            bool flag = false;
            for(v = first[u]; v != 0; v = next[v]) //前向星式遍历
            {
                if(ch[v] == s[i])
                {
                    flag = true;
                    break;
                }
            }
            if( !flag )
            {
                v = sz++; 
                total[v] = 0; //初始化
                ch[v] = s[i];
                next[v] = first[u];
                first[u] = v;
                first[v] = 0;
            }
            u = v;
            total[u]++;
        }
    }
   
    void dfs(int depth, int u, ll &ans)
    {
        if( first[u] == 0 ) ans += total[u]*(total[u]-1)*depth; //如果下面没有节点了,那么ans*=total[u]*(total[u]-1)*depth
        else
        {
            int sum = 0;
            for(int v = first[u]; v != 0; v = next[v])
                sum += total[v]*(total[u]-total[v]);  //否则计算比较次数,当前节点-相同的分支的节点的数量
            ans += (sum/2*(2*depth+1)); 
            for(int v = first[u]; v != 0; v = next[v])
                dfs(depth+1, v, ans);
        }
    }
};
int n;
char str[MAX];
Trie tr;
int main()
{
    int t = 1;
    while(~scanf("%d", &n))
    {
        if(n == 0) break;
       
        tr.clear();
        for(int i = 0; i < n; ++i)
        {
            scanf("%s", str);
            tr.insert(str);
        }
       
        ll ans = 0;
        tr.dfs(0, 0, ans);
        printf("Case %d: %lld\n", t++, ans);
    }
   
    return 0;
}
posted @ 2013-04-23 22:02  小仪在努力~  阅读(187)  评论(0)    收藏  举报