字典树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]; } };
动态分配内存模板
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]; } };
自用模板
#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; }
对串的快速检索 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); } } }
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; }
...