前缀树 / 字典树是最简单的树了
欠的总是要还的
O(n)算法的多叉树
比较好理解没啥解释的了
用数组模拟 行结点 列指针 cnt记录最大结点编号
直接上代码吧
可持久化01字典树
struct trie
{
int cnt, root[MAXN], tr[MAXN * 25][2], sum[MAXN * 25];
//结点总个数 版本进入根 树 结点内个数前缀和
trie() {cnt = 0;}
void insert(int now, int last, int val) //这一版本根下标 上一版本根下标
{
int nto, lto;
root[now] = nto = ++cnt;
lto = root[last];
for(int i = 23; i >= 0; --i)
{
int dig = val >> i & 1;
tr[nto][0] = tr[lto][0];
tr[nto][1] = tr[lto][1]; //将上一版本儿子直接覆盖
tr[nto][dig] = ++cnt, nto = cnt; //当前版本的新儿子
lto = tr[lto][dig];
sum[nto] = sum[lto] + 1; //前缀和 + 1
}
}
int get(int now, int last, int val) //这一版本根下标 上一版本根下标
{
int ret = 0;
int nto = root[now], lto = root[last];
for(int i = 23; i >= 0; --i)
{
int dig = val >> i & 1 ^ 1;
if(sum[ tr[nto][dig] ] - sum[ tr[lto][dig] ]) //当前区间内有该目标节点
ret += (1 << i), nto = tr[nto][dig], lto = tr[lto][dig];
else
nto = tr[nto][dig ^ 1], lto = tr[lto][dig ^ 1];
}
return ret;
}
}T;
int arr[MAXN];
int main()
{
for(int i = 2, t; i <= n; ++i)
T.insert(i, i - 1, arr[i]);
cout << T.get(y, x - 1, arr[n] ^ val) << '
';
return 0;
}
1.统计单词数 -> 建树
int trie[MAXN][26] = {0}, cnt = 1, ans = 0; //trie树 行结点 列指针
bool wend[MAXN] = {false}; //保存以i节点为结尾的单词是否存在
void insert(char word[])
{
int len = strlen(word), to = 1;
for(int i = 0; i < len; i++)
{
int chn = word[i] - 'a';
if(!trie[to][chn])
trie[to][chn] = ++cnt;
to = trie[to][chn];
}
if(!wend[to]) //计和
++ans;
wend[to] = true;
}
2.单词查询 -> 查询是否能在树中完全匹配
bool wend[MAXN] = {false}; //保存以i节点为结尾的单词是否存在
bool intree(char word[])
{
int len = strlen(word), to = 1;
for(int i = 0; i < len; i++)
{
int chn = word[i] - 'a';
if(! trie[to][chn])
return false;
to = trie[to][chn];
}
return wend[to]; //查询以i节点为结尾的单词是否存在
}
3.存在前缀 -> 查询是否能在树中前缀匹配
例题: http://codevs.cn/problem/4189/
bool intree(char word[]) //只要能扫到单词结尾则必定存在前缀
{
int len = strlen(word), to = 1;
for(int i = 0; i < len; i++)
{
int chn = word[i] - 'a';
if(! trie[to][chn])
return false;
to = trie[to][chn];
}
return true;
}
4.前缀统计1 -> 此单词为字典中多少单词的前缀的和
例题: http://acm.hdu.edu.cn/showproblem.php?pid=1251
int trie[MAXN][26] = {0}, sum[MAXN] = {0}, cnt = 1; //sum数组保存到此结点的前缀总和
bool wend[MAXN] = {false};
void insert(char word[])
{
int len = strlen(word), to = 1;
for(int i = 0; i < len; i++)
{
int chn = word[i] - 'a';
if(!trie[to][chn])
trie[to][chn] = ++cnt;
to = trie[to][chn];
++sum[to]; //计和
}
wend[to] = true;
}
int precnt(char word[])
{
int len = strlen(word), to = 1;
for(int i = 0; i < len; i++)
{
int chn = word[i] - 'a';
if(!trie[to][chn])
return 0; //前缀都扫不完查无此单词 和就不用想了
to = trie[to][chn];
}
return sum[to];
}
5.前缀统计2 -> 统计树中有多少单词为当前单词的前缀
例题:http://acm.ncst.edu.cn/problem.php?pid=1421
const int MAXN = 1e6 + 10;
int trie[MAXN][26] = {0}, cnt = 1;
int wend[MAXN] = {0}; //wend本用来标记单词结尾, 现在用来标记以该结点结尾的单词的数量
void insert(char word[])
{
int len = strlen(word), to = 1;
for(int i = 0; i < len; i++)
{
int chn = word[i] - 'a';
if(!trie[to][chn])
trie[to][chn] = ++cnt;
to = trie[to][chn];
}
++wend[to];
}
int precnt(char word[])
{
int len = strlen(word), to = 1, ret = 0;
for(int i = 0; i < len; i++)
{
int chn = word[i] - 'a';
if(!trie[to][chn])
break;
to = trie[to][chn];
ret += wend[to]; //路过的所有结点均为word的前缀 所以加和计算
}
return ret;
}