赞
踩
字典树是一种处理前缀的数据结构
略懂数据结构的人,相信看完下面这张图就差不多理解了
以小写字母为例,讲解以下代码实现
需要一个数组存储节点信息
外加一个mark标志,存储答案
struct Tree{
Tree() { //构造函数
mark = 0;
memset(son, 0, sizeof(son));
}
void clear() {
mark = 0;
memset(son, 0, sizeof(son));
}
int mark; //标记,一般为询问的值,视情况而定
int son[26]; //此处只考虑小写字母,即为节点存储的数据
}tree[maxn];
逐层迭代,将没有的节点插入
在末尾节点的下一个节点存储该字符串的信息(因为由这个节点前面构成的字符串是唯一的)
int root, num; //根节点永久为0
void insert(char* str) {
int position = root; //初始化位置
for (int i = 0; str[i]; i++) {
int symbol = str[i] - 'a'; //转化函数,视情况而定
if (!tree[position].son[symbol])//创建新节点
tree[position].son[symbol] = ++num;
position = tree[position].son[symbol];
}
tree[position].mark++; //记录链末尾的数量
}
逐层递归查找
int find(char* str) {
int position = root;
for (int i = 0; str[i]; i++) {
int symbol = str[i] - 'a';
if (!tree[position].son[symbol]) //找不到,返回false
return 0;
position = tree[position].son[symbol];//迭代寻找
}
return tree[position].mark; //返回相同链的数量
}
实际上,我们发现字典树是种很简单的数据结构,上面的插入和查询只是基本操作
当你理解上面的插入和查询操作后,也可以轻松实现其他操作
给出一组名字(好几个字符串)
进行m次点名,第一次字符串出现输出“OK”,第二次以上出现输出“REPEAT”,若该字符串不在给出字符串内输出“WRONG”
板子题+1
mark记录该字符串是否出现过
记录该字符串被访问次数
给出n组字符串,每组字符串有1个或多个字符串
有m组询问,询问为一个字符串,返回在哪几组字符串中出现过
板子题+2
用vector存储一个字符串在哪几组字符串中出现过
按顺序给出
n
n
n个字符串
第一个字符串最小,第二个第二,以此类推
再次给出n个字符的顺序,求逆序对数
板子题+3
字典树离散化字符串
归并排序求逆序对数
给你n个字符串,不同的排列有不同的代价,代价按照如下方式计算(字符串s的位置为x)
1.排在s后面的字符串有s的后缀,则代价为n^2
2.排在s前面的字符串有s的后缀,且没有排在s后面的s的后缀,则代价为x-y(y为最后一个与s不相等的后缀的位置);
3.s没有后缀,则代价为x
求最小代价和
显然第一种代价是不可能被用到的
只要按后缀逐个取字符就能避免第一种情况
处理后缀比较麻烦,我们将字符串翻转,就可以用字典树存储前缀
按前缀逐个取字符串
容易证明先取后缀少的字符串,最终代价少
先重构树,只保留存在的字符串
先
d
f
s
dfs
dfs处理各个子树的节点数
再根据子树权重,优先对权重小的子树搜索即可
//具体过程,类似树链剖分
#include <iostream> #include <cstdio> #include <algorithm> #include <cstring> using namespace std; #pragma warning (disable:4996) const int maxn = 1000005; struct Tree{ Tree() { mark = 0; memset(son, 0, sizeof(son)); } void clear() { mark = 0; memset(son, 0, sizeof(son)); } int mark; //标记 int son[26]; //此处只考虑小写字母 }tree[maxn]; int root, num; //根节点永久为0 void insert(char* str) { int position = root; //初始化位置 for (int i = 0; str[i]; i++) { int symbol = str[i] - 'a'; //转化函数,视情况而定 if (!tree[position].son[symbol])//创建新节点 tree[position].son[symbol] = ++num; position = tree[position].son[symbol]; } tree[position].mark = 1; //记录链末尾的数量 } int find(char* str) { int position = root; for (int i = 0; str[i]; i++) { int symbol = str[i] - 'a'; if (!tree[position].son[symbol]) //找不到,返回false return 0; position = tree[position].son[symbol];//迭代寻找 } return tree[position].mark++; //返回相同链的数量 } char s[65]; int main() { ios::sync_with_stdio(false); cin.tie(nullptr); cout.tie(nullptr); int n, m; cin >> n; for (int i = 1; i <= n; i++) { cin >> s; insert(s); } cin >> m; while (m--) { cin >> s; int res = find(s); if (res == 0)cout << "WRONG\n"; else if (res == 1)cout << "OK\n"; else cout << "REPEAT\n"; } }
#include <iostream> #include <cstdio> #include <algorithm> #include <cstring> #include <vector> using namespace std; #pragma warning (disable:4996) const int maxn = 1000005; struct Tree{ Tree() { memset(son, 0, sizeof(son)); } void clear() { id.clear(); memset(son, 0, sizeof(son)); } vector<int> id; int son[26]; //此处只考虑小写字母 }tree[maxn]; int root, num; //根节点永久为0 void insert(char* str, int x) { int position = root; //初始化位置 for (int i = 0; str[i]; i++) { int symbol = str[i] - 'a'; //转化函数,视情况而定 if (!tree[position].son[symbol])//创建新节点 tree[position].son[symbol] = ++num; position = tree[position].son[symbol]; } if (tree[position].id.size() == 0 || x != tree[position].id[tree[position].id.size() - 1]) tree[position].id.push_back(x); } void find(char* str) { int position = root; for (int i = 0; str[i]; i++) { int symbol = str[i] - 'a'; if (!tree[position].son[symbol]) //找不到,返回false { printf("\n"); return; } position = tree[position].son[symbol];//迭代寻找 } for (int i = 0; i < tree[position].id.size(); i++) if (i)printf(" %d", tree[position].id[i]); else printf("%d", tree[position].id[i]); printf("\n"); } char s[30]; int main() { ios::sync_with_stdio(false); cin.tie(nullptr); cout.tie(nullptr); int n, m; cin >> n; for (int i = 1; i <= n; i++) { cin >> m; while (m--) { cin >> s; insert(s, i); } } cin >> m; while (m--) { cin >> s; find(s); } }
#include <iostream> #include <cstdio> #include <algorithm> #include <cstring> using namespace std; #pragma warning (disable:4996) typedef long long LL; const int maxn = 100005; struct Tree{ Tree() { mark = 0; memset(son, 0, sizeof(son)); } void clear() { mark = 0; memset(son, 0, sizeof(son)); } int mark; //标记 int son[52]; //此处只考虑小写字母 }tree[maxn << 2]; int root, num; //根节点永久为0 void insert(char* str, int x) { int position = root; //初始化位置 for (int i = 0; str[i]; i++) { int symbol; if (str[i] >= 'A' && str[i] <= 'Z')symbol = str[i] - 'A'; else symbol = str[i] - 'a' + 26; if (!tree[position].son[symbol])//创建新节点 tree[position].son[symbol] = ++num; position = tree[position].son[symbol]; } tree[position].mark = x; //记录链末尾的数量 } int find(char* str) { int position = root; for (int i = 0; str[i]; i++) { int symbol; if (str[i] >= 'A' && str[i] <= 'Z')symbol = str[i] - 'A'; else symbol = str[i] - 'a' + 26; if (!tree[position].son[symbol]) //找不到,返回false return 0; position = tree[position].son[symbol];//迭代寻找 } return tree[position].mark; //返回相同链的数量 } char s[20]; int a[maxn]; LL ans = 0; void merge(int left, int right, int stdl, int stdr) { if (left == stdr) return; int i = left, j = stdl, k = 0; int* t = new int[stdr - left + 1]; while (i <= right && j <= stdr) { if (a[i] < a[j]) t[k++] = a[i++]; else { t[k++] = a[j++]; ans = ans + right - i + 1; } } while (i <= right)t[k++] = a[i++]; while (j <= stdr)t[k++] = a[j++]; for (int i = left; i <= stdr; i++)a[i] = t[i - left]; delete []t; } void merge_sort(int left, int right) { if (left == right) return; int mid = (left + right) >> 1; merge_sort(left, mid); merge_sort(mid + 1, right); merge(left, mid, mid + 1, right); } int main() { ios::sync_with_stdio(false); cin.tie(nullptr); cout.tie(nullptr); int n; cin >> n; for (int i = 1; i <= n; i++) { cin >> s; insert(s, i); } for (int i = 1; i <= n; i++) { cin >> s; a[i] = find(s); } merge_sort(1, n); printf("%lld\n", ans); }
#include <iostream> #include <cstdio> #include <algorithm> #include <cstring> #include <vector> using namespace std; #pragma warning (disable:4996) typedef long long LL; const int maxn = 100005; struct Tree{ Tree() { mark = 0; memset(son, 0, sizeof(son)); } void clear() { mark = 0; memset(son, 0, sizeof(son)); } bool mark; //标记 int son[26]; //此处只考虑小写字母 }tree[maxn << 3]; int root, num; //根节点永久为0 void insert(char* str) { int position = root; //初始化位置 for (int i = 0; str[i]; i++) { int symbol = str[i] - 'a'; //转化函数,视情况而定 if (!tree[position].son[symbol])//创建新节点 tree[position].son[symbol] = ++num; position = tree[position].son[symbol]; } tree[position].mark = true; //记录链末尾的数量 } int find(char* str) { int position = root; for (int i = 0; str[i]; i++) { int symbol = str[i] - 'a'; if (!tree[position].son[symbol]) //找不到,返回false return 0; position = tree[position].son[symbol];//迭代寻找 } return tree[position].mark; //返回相同链的数量 } char s[maxn * 6]; vector<int> E[maxn]; int cnt; void struct_dfs(int root, int f) { for (int i = 0; i < 26; i++) { if (!tree[root].son[i]) continue; if (!tree[tree[root].son[i]].mark) struct_dfs(tree[root].son[i], f); else { E[f].push_back(++cnt); struct_dfs(tree[root].son[i], cnt); } } } int n_size[maxn]; bool cmp(int x, int y) { return n_size[x] < n_size[y]; } void dfs1(int now, int f) { n_size[now] = 1; for (int i = 0; i < E[now].size(); i++) { dfs1(E[now][i], now); n_size[now] += n_size[E[now][i]]; } sort(E[now].begin(), E[now].end(), cmp); } int dfn, d[maxn]; LL ans = 0; void dfs2(int now, int f) { d[now] = ++dfn; ans = ans + d[now] - d[f]; for (int i = 0; i < E[now].size(); i++) dfs2(E[now][i], now); } int main() { ios::sync_with_stdio(false); cin.tie(nullptr); cout.tie(nullptr); int n; cin >> n; while (n--) { cin >> s; reverse(s, s + strlen(s)); insert(s); } struct_dfs(0, 0); dfs1(0, -1); dfs2(0, 0); cout << ans << '\n'; }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。