字符串 (1)--- 字典树

/*
    Trie树,是一种树形结构,是一种哈希树的变种。
    应用于统计,排序和保存大量的字符串(但不仅限于字符串,
    经常被搜索引擎系统用于文本词频统计。
    它的优点是:利用字符串的公共前缀来减少查询时间,最大限度地减少无谓的字符串比较,查询效率比哈希树高。
    特点: 根节点不包含字符,除根节点外每一个节点都只包含一个字符; 
    从根节点到某一节点,路径上经过的字符连接起来,为该节点对应的字符串; 每个节点的所有子节点包含的字符都不相同。

    https://www.luogu.com.cn/problem/P2580

*/


#include
#include
using namespace std;
const int MAXN = 500001;
int nex[MAXN][26];
int tag[MAXN];  // 该结点结尾的字符串出现次数
int cnt = 0;

void insert(char *s) // 插入字符串
{  
    int p = 0;
    for (int i = 0; s[i]; i++)
    {
        int c = s[i] - 'a';
        if (!nex[p][c]) nex[p][c] = ++cnt;  // 如果没有,就添加结点
        p = nex[p][c];
    }
    tag[p] = 1;
}

int find(char *s) // 查找字符串
{
    int p = 0;
    for (int i = 0; s[i]; i++)
    {
        int c = s[i] - 'a';
        if (!nex[p][c]) break;
        p = nex[p][c];
    }
    return p;
}

int main()
{
    int i, n, m, t;
    cin >> n;
    char name[52] = { '\0' };

    for (i = 0; i < n; ++i)
    {
        cin >> name;
        insert(name);
    }

    cin >> m;
    for (i = 0; i < m; ++i)
    {
        cin >> name;
        t = find(name);
        if (tag[t] == 0)
            cout << "WRONG" << endl;
        else if (tag[t] == 1)
        {
            cout << "OK" << endl;
            tag[t] = 2;
        }
        else
        {
            cout << "REPEAT" << endl;
        }
    }

    return 0;
}
 

/*
    https://www.luogu.com.cn/problem/P4551

    随便指定一个根 root,用T(u, v)表示u 和 v 之间的路径的边权异或和,
    那么T(u,v)=T(root, u) ^ T(root,v),因为 LCA 以上的部分异或两次抵消了。
    将所有T(root, u)插入到一棵Trie中,就可以求出某个T(root, u)与T(root, v)的最大异或和。
*/

#include
using namespace std;

const int MAXN = 100002;
// 链式前向星存储数
struct node {
    int to;  // 连接点的编号
    int next;// 下一条邻接边的edge下标
    int w;   // 代表边的权值
} edge[MAXN << 1];
// head[u]: u连接的第一(最后)条边的edge下标
int cnt = 0, head[MAXN] = { 0 };

void add(int u, int v, int w)
{
    edge[++cnt].next = head[u];  // 采用头插法
    head[u] = cnt;
    edge[cnt].to = v;
    edge[cnt].w = w;
}


// dis[i]: i节点到根节点的边权异或和
int dis[MAXN] = { 0 };
void dfs(int u, int fa)
{
    for (int i = head[u]; i; i = edge[i].next)
    {
        int v = edge[i].to;
        if (v == fa)
            continue;
        dis[v] = dis[u] ^ edge[i].w;
        dfs(v, u);
    }
}

int index = 0;
// 权值为int, int占4个字节,即32位, 按dis[i]二进制数建trie,由二进制的高位到低位依次插入
int nxt[MAXN << 5][2];

void insert(int x)
{
    int u = 0, c = 0;
    // 从高位到低位截取边权异或和
    for (int i = 31; i >=0; --i)
    {
        c = ((x >> i) & 1);
        if (!nxt[u][c])
            nxt[u][c] = ++index;
        u = nxt[u][c];
    }
}

/* 
贪心选择:为了求最大的异或值,从高位到低位扫描二进制x, 每次向与当前位数值不同的节点走,
         如果能这么走,res的这一位为1;如果不这么走,res的这一位为0。
         最后走到叶节点时,此时的res就是某个dis[x]与dis[y]异或得到的最大值
*/
int get(int x)
{
    int res = 0, u = 0, c = 0;
    // 从高位到低位截取边权异或和
    for (int i = 31; i >=0; --i)
    {
        c = ((x >> i) & 1);
        if (nxt[u][c^1]) // 如果能向和当前位不同的子树走,就向那边走
        {
            u = nxt[u][c^1];
            res |= (1 << i);
        }
        else
            u = nxt[u][c];
    }

    return res;
}

int main()
{
    int i, n, u, v, w;
    cin >> n;
    for (i = 1; i < n; ++i)
    {
        cin >> u >> v >> w;
        add(u, v, w);
        add(v, u, w);
    }

    dfs(1, 0);
    for (i = 1; i <= n; ++i)
        insert(dis[i]);

    int sum = 0;
    for (i = 1; i <= n; ++i)
        sum = max(sum, get(dis[i]));

    cout << sum << endl;

    return 0;
}
 

你可能感兴趣的:(算法)