Tire 字典树 + 并查集 + 字符串哈希

1.Tire字典树 

(1)基本概念

Tire树 : 高效地存储和查找字符串集合的数据结构

                

(2)题目示例 : Trie字符串统计 

①题目介绍

                 

②解题思路

③代码示例

#include<iostream>

using namespace std;

const int N = 100010;

int son[N][26]; //用于存储节点
int count[N]; //存储以某个字符结尾的单词的数量
int Next = 0; //记录下一个单词节点是第几个 ; 下标是0的点,既是空节点又是根节点
char str[N]; //存储字符串


void insert(char* str)
{
    int p = 0;
    for(int i = 0 ; str[i];++i) //str最后一位是0
    {
        int index = str[i]-'a'; //字符映射位置
        if(!son[p][index]) son[p][index] = ++Next; //该位置为空,分配一个节点
        p = son[p][index]; 
    }
    
    //每个结尾单词在Tire树 结构中的节点是唯一的,所以 next记录节点的唯一性,找到同一个next就能统计相同单词个数
    count[p]++; //以该字符结尾的单词,统计次数
}

int query(char* str)
{
    int p = 0;
    for(int i = 0 ; str[i] ; ++i)
    {
        int index = str[i]-'a';
        if(!son[p][index]) return 0;  //节点路径中不存在该字符节点说明没有该字符串
        p = son[p][index];
    }
    
    return count[p];
}


int main()
{
    int m;
    cin >> m;
    
    while(m--)
    {
        char op;
        cin >> op >> str;
        
        if(op == 'I') insert(str);
        else cout << query(str) << endl;
    }
    
    
    return 0;
}

                

(3)题目示例 : 最大异或对

                

#include<iostream>
#include<algorithm>
#include<vector>

using namespace std;

const int N = 100010;
const int M = N*31; //31位二进制,1位符号位没用

int a[N];
int son[M][2]; //M代表最大限度存储二进制位数
int Next;    

void insert(int x)
{
    int p = 0;
    for(int i = 30 ; i >=0 ;--i) //从高位到低位走,最大异或对
    {
        int index = x >> i & 1; //第i位 是0/1
        if(!son[p][index]) son[p][index] = ++Next;
        p = son[p][index];
    }
}

int query(int x)
{
   int p = 0;
   int res = 0; //计算与x 异或得到最大值的 值
   for(int i = 30 ; i >= 0 ;--i)
   {
       int index = x >> i & 1;
       if(son[p][!index]) // 找与x二进制相反的数
       {
           p = son[p][!index];
           res = res*2 + !index;
       }
       else
       {
           p = son[p][index];
           res = res*2 + index;
       }
   }
   
   return res;
}

int main()
{
    int n;
    cin >> n;
    
    for(int i = 0 ; i < n ;++i) cin >> a[i];
    
    int res = 0;
    for(int i= 0 ; i < n;++i)
    {
        insert(a[i]);
        
        res = max(res,a[i] ^ query(a[i]));
    }
    cout << res << endl;
    
    //这里不管是先全部插入再查询 还是 便插入边查询 是一样的
    //某个数都要 31次查找过程
    
    return 0;
}

                              

                   

2.并查集

(1)基本概念


                

(2)题目示例 : 合并集合

                 

#include<iostream>
#include<vector>

using namespace std;


//找集合的root节点 + 路径压缩(所有子节点都指向该集合的根节点)
int FindRoot(vector<int>& ufs,int x)
{
    if(ufs[x] != x) ufs[x] = FindRoot(ufs,ufs[x]);
    return ufs[x];
}
    

int main()
{
    int n,m;
    cin >> n >> m;
    
    vector<int> ufs(n+1,0); // 0号下标位置没用
    for(int i = 1 ; i <= n ; ++i) ufs[i] = i;   //初始化
    
    while(m--)
    {
        char op;
        int a,b;
        cin >> op >> a >> b;
        if(op == 'M') ufs[FindRoot(ufs,a)] = FindRoot(ufs,b);
        else
        {
            if(FindRoot(ufs,a) == FindRoot(ufs,b)) cout << "Yes" << endl;
            else cout << "No" << endl;
        }
    }
    return 0;
}

                            

(3)题目示例 : 连通块中点的数量

                 

#include<iostream>
#include<vector>

using namespace std;

//查找根节点+ 路径压缩
int FindRoot(vector<int>& ufs,int x)
{
    if(ufs[x] != x) ufs[x] = FindRoot(ufs,ufs[x]);
    return ufs[x];
}

int main()
{
    int n,m;
    cin >> n >> m;
    
    vector<int> count(n+1,1); //以某个下标为根的集合数量
    vector<int> ufs(n+1);
    for(int i = 1 ; i <= n ; ++i) ufs[i] = i;
    
    while(m--)
    {
        char op[5];
        int a,b;
        
        cin >> op ;
        if(op[0] == 'C') 
        {
            cin >> a >> b;
            int root1 = FindRoot(ufs,a);
            int root2 = FindRoot(ufs,b);
            if(root1 == root2) continue;  //很重要,否则count重复添加
            
            ufs[root1] = root2;
            count[root2] += count[root1];
        }
        else if(op[1] == '1')
        {
            cin >> a >> b;
            if(FindRoot(ufs,a) == FindRoot(ufs,b)) cout << "Yes" << endl;
            else cout << "No" << endl;
        }
        else
        {
            cin >> a;
            cout << count[FindRoot(ufs,a)] << endl;
        }
    }
    
    
    
    
    return 0;
}

                

3.字符串哈希

                 

(1)题目示例

         

#include<iostream>
#include<vector>
#include<string>

using namespace std;

//使用常规的解法超出时间限制
typedef unsigned long long ULL;
const int P = 131; //经过计算,这个数的冲突概率低

ULL get(vector<ULL>& h,vector<ULL>& p,int l,int r)
{
    return h[r] - h[l-1] * p[r-l+1]; 
}

int main()
{
    int n,m;
    cin >> n >> m;
    
    string str;
    cin >> str;
    
    vector<ULL> h(n+1,0); //h[i] 表示前i个字符的哈希值
    vector<ULL> p(n+1,0); //p[i] 表示p的i次方
    
    p[0] = 1;
    for(int i = 1; i <= n ;++i)
    {
        p[i] = p[i-1] * P;
        h[i] = h[i-1] * P + str[i-1]; //求字符串的前缀和
    }
    
    int l1,r1,l2,r2;
    while(m--)
    {
        cin >> l1 >> r1 >> l2 >> r2;
        if(get(h,p,l1,r1) == get(h,p,l2,r2)) cout << "Yes" << endl;
        else cout << "No" << endl;
    }
    
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值