字典树是一种处理前缀的数据结构
略懂数据结构的人,相信看完下面这张图就差不多理解了
以小写字母为例,讲解以下代码实现
需要一个数组存储节点信息
外加一个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
#include
#include
#include
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
#include
#include
#include
#include
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
#include
#include
#include
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
#include
#include
#include
#include
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';
}