字典树trie树

目的:已知n个长度不一定相同的母串,以及一个长度为m的模式串T,求该模式串是否是一个母串的前缀。

时间复杂度:O(m)

特点:牺牲空间换取时间,常用于字符串的快速检索,快速排序与去重,文本的词频统计等。

 

模板

const int MAX_N = 10000;  // Trie 树上的最大结点数
const int MAX_C = 26;  // 每个结点的子结点个数上限
struct Trie {
    int ch[MAX_N][MAX_C];  // ch 保存了每个结点的 26 个可能的子结点编号,26 对应着 26 种小写字母,也就是说,插入的字符串全部由小写字母组成。初始全部为 -1
    int tot;  // 总结点个数(不含根结点),初始为 0
    int cnt[MAX_N];  // 以当前结点为终端结点的 Trie 树中的字符串个数

    void init() {  // 初始化 Trie 树,根结点编号始终为 0
        tot = 0;
        memset(cnt, 0, sizeof(cnt));
        memset(ch, -1, sizeof(ch));
    }

    void insert(char *str) {
        int p = 0;  // 从根结点(0)出发
        for (int i = 0; str[i]; ++i) {
            if (ch[p][str[i] - 'a'] == -1) {  // 该子结点不存在
                ch[p][str[i] - 'a'] = ++tot;  // 新增结点
            } 
            p = ch[p][str[i] - 'a'];  // 在 Trie 树上继续插入字符串 str
        }
        cnt[p]++;
    }

    int find(char *str) {  // 返回字符串 str 的出现次数
        int p = 0;
        for (int i = 0; str[i]; ++i) {
            if (ch[p][str[i] - 'a'] == -1) {
                return 0;
            }
            p = ch[p][str[i] - 'a'];
        }
        return cnt[p];
    }
};
View Code

动态分配内存模板

const int MAX_N = 10000;  // Trie 树上的最大结点数
const int MAX_C = 26;  // 每个结点的子结点个数上限
struct Trie {
    int *ch[MAX_N];  // ch 保存了每个结点的 26 个可能的子结点编号,26 对应着 26 种小写字母,也就是说,插入的字符串全部由小写字母组成。初始全部为 -1
    int tot;  // 总结点个数(不含根结点),初始为 0
    int cnt[MAX_N];  // 以当前结点为终端结点的 Trie 树中的字符串个数

    void init() {  // 初始化 Trie 树,根结点编号始终为 0
        tot = 0;
        memset(cnt, 0, sizeof(cnt));
        memset(ch, 0, sizeof(ch));  // 将 ch 中的元素初始化为 NULL
    }

    void insert(char *str) {
        int p = 0;  // 从根结点(0)出发
        for (int i = 0; str[i]; ++i) {
            if (ch[p] == NULL) {
                ch[p] = new int[MAX_C];  // 只有 p 当包含子结点的时候才去开辟 ch[p] 的空间
                memset(ch[p], -1, sizeof(int) * MAX_C);  // 初始化为 -1
            }
            if (ch[p][str[i] - 'a'] == -1) {  // 该子结点不存在
                ch[p][str[i] - 'a'] = ++tot;  // 新增结点
            } 
            p = ch[p][str[i] - 'a'];  // 在 Trie 树上继续插入字符串 str
        }
        cnt[p]++;
    }

    int find(char *str) {  // 返回字符串 str 的出现次数
        int p = 0;
        for (int i = 0; str[i]; ++i) {
            if (ch[p][str[i] - 'a'] == -1) {
                return 0;
            }
            p = ch[p][str[i] - 'a'];
        }
        return cnt[p];
    }
};
View Code

自用模板

#include <iostream>
#include <string.h>
using namespace std;
const int maxn=10010;
struct Trie{
    int ch[maxn][26];
    int cnt[maxn];
    int num;
    void init()
    {
        memset(ch[0],0,sizeof(ch[0]));
        cnt[0]=0;
        num=0;
    }
    int newnode()
    {
        ++num;
        memset(ch[num],0,sizeof(ch[num]));
        cnt[num]=0;
        return num;
    }
    void insert(char *s)
    {
        int u=0;
        for(int i=0;s[i];i++)
        {
            if(!ch[u][s[i]-'a'])
            {
                ch[u][s[i]-'a']=newnode();
            }
            u=ch[u][s[i]-'a'];
            ++cnt[u];
        }
    }
    int query(char *s)
    {
        int u=0;
        for(int i=0;s[i];i++)
        {
            if(!ch[u][s[i]-'a'])
            {
                return 0;
            }
            u=ch[u][s[i]-'a'];
        }
        return cnt[u];
    }
}trie;

int main() 
{
    trie.init();
    trie.insert("china");
    trie.insert("chinese");
    trie.insert("children");
    trie.insert("check");
    
    cout<<trie.query("")<<endl;
    cout<<trie.query("ch")<<endl;
    cout<<trie.query("chi")<<endl;
    cout<<trie.query("chin")<<endl;
    cout<<trie.query("china")<<endl;
    cout<<trie.query("beijing")<<endl;
    return 0;
}
View Code

 

对串的快速检索 Trie树

串的排序 Trie树+dfs

char now[MAX_LEN];
void dfs(int p, int len) {
    if (cnt[p] > 0) {
        now[len] = '\0';
        while (cnt[p] --> 0) {
            cout << now << endl;
        }
    }
    for (int i = 0; i < 26; ++i) {
        if (ch[p][i]) {
            now[len] = 'a' + i;
            dfs(ch[p][i], len + 1);
        }
    }
}
View Code

 n个字符串,找出最长的len,使得s1到sn中,si是si+1的前缀,且si是si+1的后缀

exkmp处理每个字符串中哪些长度的前缀和后缀相同,trie插入时动态更新最优解,开的trie树需要动态分配内存

#include<bits/stdc++.h>
using namespace std;
const int maxn=2e6+10;
int dp[maxn];

struct trie
{
    int *ch[maxn];
    int cnt[maxn];
    int tot;
    void init()
    {
        memset(ch,0,sizeof ch);
        memset(cnt,0,sizeof cnt);
        tot=0;
    }
    void insert(char *s,int *next,int idx)
    {
        int p=0,n=strlen(s);
        for(int i=0;s[i];i++)
        {
            if(ch[p]==NULL)
            {
                ch[p]=new int[26];
                memset(ch[p],-1,sizeof(int)*26);
            }
            if(ch[p][s[i]-'A']==-1)
            {
                ch[p][s[i]-'A']=++tot;
            }
            p=ch[p][s[i]-'A'];
            if(cnt[p])
            {
                if(next[n-i-1]==i+1)
                dp[idx]=max(dp[idx],dp[cnt[p]]+1);
            }
        }
        cnt[p]=idx;
    }
}trie;

void getnext(char *s,int *next)
{
    int n=strlen(s),i,j,k;
    next[0]=n;
    for(j=0;1+j<n&&s[j]==s[1+j];j++);next[1]=j;
    k=1;
    for(i=2;i<n;i++)
    {
        int len=k+next[k],l=next[i-k];
        if(l<len-i)next[i]=l;
        else
        {
            for(j=max(0,len-i);i+j<n&&s[j]==s[i+j];j++);
            next[i]=j;
            k=i;
        }
    }
}


char ch[maxn];
int Next[maxn];
int main()
{
    int n;
    scanf("%d",&n);
    for(int i=0;i<=n;i++)dp[i]=1;
    for(int i=1;i<=n;i++)
    {
        scanf("%s",ch);
        getnext(ch,Next);
        trie.insert(ch,Next,i);
    }
    int ans=0;
    for(int i=1;i<=n;i++)
    {
        ans=max(ans,dp[i]);
    }
    cout<<ans<<endl;
    return 0;
}
View Code

 

...

 

posted @ 2019-09-06 08:09  myrtle  阅读(150)  评论(0编辑  收藏  举报