《算法竞赛进阶指南》1.6Trie

142. 前缀统计

给定N个字符串S1,S2…SN,接下来进行M次询问,每次询问给定一个字符串T,求S1~SN中有多少个字符串是T的前缀。
输入字符串的总长度不超过106,仅包含小写字母。

输入格式
第一行输入两个整数N,M。
接下来N行每行输入一个字符串Si。
接下来M行每行一个字符串T用以询问。

输出格式
对于每个询问,输出一个整数表示答案。
每个答案占一行。

输入样例:
3 2
ab
bc
abc
abc
efg

输出样例:
2
0

#include <iostream>

using namespace std;

const int N = 1000010, M = 500000; //trie 的长度

int n, m;
int son[M][26], cnt[N], idx; //cnt记录该节点是多少个字符串的末尾节点 idx 虚拟内存的指针(当前用到的数组的地址,当前节点的下标)
char str[N];

int query() //查询 字符串前缀个数
{
    int p = 0, res = 0; //res 表示当前路径所有单词的总和
    for(int i = 0; str[i]; i++)
    {
        int &s = son[p][str[i] - 'a'];
        if(!s) break; //当前节点为空 没有下面的路径 break
        p = s;
        res += cnt[p]; //加上以当前节点为结尾的个数
    }
    return res;
}

void insert() //插入
{
    int p = 0; //trie根节点  0号节点
    for(int i = 0; str[i]; i++)
    {
        int &s = son[p][str[i] - 'a']; //当前儿子
        if(!s) s = ++ idx; //s 不存在 分配一个新的值
        p = s; 
    }
    cnt[p] ++; //有一个单词以p节点为结尾 加1
}

int main()
{
    scanf("%d%d", &n, &m); //输入规模大于100万用scanf()
    
    while(n --)
    {
        scanf("%s", str);
        insert();
    }
    
    while(m --)
    {
        scanf("%s", str);
        printf("%d\n", query());
    }
    return 0;
}

143. 最大异或对

在给定的N个整数A1,A2……AN中选出两个进行xor(异或)运算,得到的结果最大是多少?

输入格式
第一行输入一个整数N。
第二行输入N个整数A1~AN。

输出格式
输出一个整数表示答案。

数据范围
1≤N≤105,
0≤Ai<231

输入样例:
3
1 2 3

输出样例:
3

#include <iostream>
#include <algorithm>

using namespace std;

const int N = 100010, M = 3000000; //M节点个数

int n;
int son[M][2],idx;
int a[N];

void insert(int x)
{
    int p = 0;
    for(int i = 30; ~i; i--) //i >= 0 等价于 ~i
    {
        int &s = son[p][x >> i & 1]; //取出二进制第i位 看第i位是0还是1
        if(!s) s = ++ idx; //创建新节点
        p = s; 
    }
}

int query(int x)
{
    int res = 0, p = 0;
    for(int i = 30; ~i; i--)
    {
        int s = x >> i & 1;
        if(son[p][!s]) //查看不一样的分支是否存在 1看0 0看1
        {
            res += 1 << i; //当前位对res所做出的贡献
            p = son[p][!s];
        }
        else p = son[p][s];
    }
    return res;
}

int main()
{
    cin >> n;
    for(int i = 0; i < n; i++) 
    {
        cin >> a[i];
        insert(a[i]);
    }
    int res = 0;
    for(int i = 0; i < n; i++) res = max(res, query(a[i])); //query 找到和当前异或最大的结果
    
        
            
    cout << res << endl;
    return 0;
}

144. 最长异或值路径

给定一个树,树上的边都具有权值。
树中一条路径的异或长度被定义为路径上所有边的权值的异或和:
在这里插入图片描述
⊕ 为异或符号。
给定上述的具有n个节点的树,你能找到异或长度最大的路径吗?

输入格式
第一行包含整数n,表示树的节点数目。
接下来n-1行,每行包括三个整数u,v,w,表示节点u和节点v之间有一条边权重为w。

输出格式
输出一个整数,表示异或长度最大的路径的最大异或和。

数据范围
1≤n≤100000,
0≤u,v<n,
0≤w<231

输入样例:
4
0 1 3
1 2 4
1 3 6

输出样例:
7

样例解释
样例中最长异或值路径应为0->1->2,值为7 (=3 ⊕ 4)

#include <iostream>
#include <algorithm>
#include <cstring>

using namespace std;

const int N = 100010, M = 3000000; //M节点个数

int n;
int h[N], e[N * 2], c[N * 2], ne[N * 2], cnt; //树:数组模拟邻接表来存,e无向图,边数*2,c是边权,ne存next指针,cnt内存,当前位下标
int son[M][2],idx;
int a[N];

void add(int u, int v, int w) //把u,v加到图里面去
{
    e[cnt] = v, c[cnt] = w, ne[cnt] = h[u], h[u] = cnt ++; //数组模拟邻接表
}

void dfs(int u, int father, int sum)
{
    a[u] =sum;
    for(int i = h[u]; ~i; i = ne[i]) //遍历u这一点的所有儿子
    {
        int j = e[i]; //j表示儿子
        if( j != father) //这条边的出边不是往上走的
            dfs(j, u, sum ^ c[i]);
    }
}

void insert(int x)
{
    int p = 0;
    for(int i = 30; ~i; i--) //i >= 0 等价于 ~i
    {
        int &s = son[p][x >> i & 1]; //取出二进制第i位 看第i位是0还是1
        if(!s) s = ++ idx; //创建新节点
        p = s; 
    }
}

int query(int x)
{
    int res = 0, p = 0;
    for(int i = 30; ~i; i--)
    {
        int s = x >> i & 1;
        if(son[p][!s]) //查看不一样的分支是否存在 1看0 0看1
        {
            res += 1 << i; //当前位对res所做出的贡献
            p = son[p][!s];
        }
        else p = son[p][s];
    }
    return res;
}

int main()
{
    cin >> n;
    memset(h, -1, sizeof h); //初始化邻接表表头
    for(int i = 0; i < n - 1; i++)
    {
        int u, v, w;
        cin >> u >> v >> w;
        add(u, v, w);
        add(v, u, w); //无向图 加两条边
    }
    
    dfs(0, -1, 0); //从根节点0开始,-1 无向图遍历常用方式 出边等于父节点 不回溯 ,sum 开始为 0
    
    for(int i = 0; i < n; i++) insert(a[i]); //根节点到i的路径所有边权的xor值
    
    int res = 0;
    for(int i = 0; i < n; i++) res = max(res, query(a[i])); //query 找到和当前异或最大的结果
    
        
            
    cout << res << endl;
    return 0;
}
posted @ 2019-05-26 16:09  WMXNLFD  阅读(252)  评论(0编辑  收藏  举报