字典树模板

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;

}

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