【第二讲】数据结构(3)

简介: 【第二讲】数据结构(3)

2.11哈希表

一般哈希 —— 模板题 AcWing 840. 模拟散列表


(1) 拉链法


int h[N], e[N], ne[N], idx;
   // 向哈希表中插入一个数
   void insert(int x)
   {
       int k = (x % N + N) % N;
       e[idx] = x;
       ne[idx] = h[k];
       h[k] = idx ++ ;
   }
   // 在哈希表中查询某个数是否存在
   bool find(int x)
   {
       int k = (x % N + N) % N;
       for (int i = h[k]; i != -1; i = ne[i])
           if (e[i] == x)
               return true;
       return false;
   }

(2) 开放寻址法


int h[N];
    // 如果x在哈希表中,返回x的下标;如果x不在哈希表中,返回x应该插入的位置
    int find(int x)
    {
        int t = (x % N + N) % N;
        while (h[t] != null && h[t] != x)
        {
            t ++ ;
            if (t == N) t = 0;
        }
        return t;
    }

字符串哈希 —— 模板题 AcWing 841. 字符串哈希


核心思想:将字符串看成P进制数,P的经验值是131或13331,取这两个值的冲突概率低


小技巧:取模的数用2^64,这样直接用unsigned long long存储,溢出的结果就是取模的结果


typedef unsigned long long ULL;
ULL h[N], p[N]; // h[k]存储字符串前k个字母的哈希值, p[k]存储 P^k mod 2^64
// 初始化
p[0] = 1;
for (int i = 1; i <= n; i ++ )
{
    h[i] = h[i - 1] * P + str[i];
    p[i] = p[i - 1] * P;
}
// 计算子串 str[l ~ r] 的哈希值
ULL get(int l, int r)
{
    return h[r] - h[l - 1] * p[r - l + 1];
}

2.11.1 840. 模拟散列表

维护一个集合,支持如下几种操作:


I x,插入一个数 x;


Q x,询问数 x 是否在集合中出现过;


现在要进行 N 次操作,对于每个询问操作输出对应的结果。


输入格式


第一行包含整数 N,表示操作数量。


接下来 N 行,每行包含一个操作指令,操作指令为 I x,Q x 中的一种。


输出格式


对于每个询问指令 Q x,输出一个询问结果,如果 x 在集合中出现过,则输出 Yes,否则输出 No。


每个结果占一行。


数据范围


1≤N≤105


−109≤x≤109


输入样例:


5


I 1


I 2


I 3


Q 2


Q 5


输出样例:


Yes


No


#include<bits/stdc++.h>
using namespace std;
map<int,bool> mp;
int n;
int main()
{
    cin>>n;
    while(n--)
    {
        char op;
        int x;
        cin>>op>>x;
        if(op=='I') mp[x]=true;
        else
        {
            if(mp.count(x)) cout<<"Yes"<<endl;
            else cout<<"No"<<endl;
        }
    }
    return 0;
}

2.11.2 841. 字符串哈希

给定一个长度为 n 的字符串,再给定 m 个询问,每个询问包含四个整数 l1,r1,l2,r2,请你判断 [l1,r1] 和 [l2,r2] 这两个区间所包含的字符串子串是否完全相同。


字符串中只包含大小写英文字母和数字。


输入格式


第一行包含整数 n 和 m,表示字符串长度和询问次数。


第二行包含一个长度为 n 的字符串,字符串中只包含大小写英文字母和数字。


接下来 m 行,每行包含四个整数 l1,r1,l2,r2,表示一次询问所涉及的两个区间。


注意,字符串的位置从 1 开始编号。


输出格式


对于每个询问输出一个结果,如果两个字符串子串完全相同则输出 Yes,否则输出 No。


每个结果占一行。


数据范围


1≤n,m≤105


输入样例:


8 3


aabbaabb


1 3 5 7


1 3 6 8


1 2 1 2


输出样例:


Yes


No


Yes


#include<bits/stdc++.h>
using namespace std;
typedef unsigned long long ULL;
const int N=100010,P=131;
string str;
int n,m;
ULL h[N],p[N];
ULL hashstr(int l,int r)
{
    return h[r]-h[l-1]*p[r-l+1];
}
int main()
{
    cin>>n>>m;
    cin>>str;
    p[0]=1;
    for(int i=1;i<=n;i++)
    {
        h[i]=h[i-1]*P+str[i-1];
        p[i]=p[i-1]*P;
    }
    while(m--)
    {
        int l,r,ll,rr;
        cin>>l>>r>>ll>>rr;
        if(hashstr(l,r)==hashstr(ll,rr)) cout<<"Yes"<<endl;
        else cout<<"No"<<endl;
    }
    return 0;
}

2.12 STL

C++ STL简介


vector, 变长数组,倍增的思想
    size()  返回元素个数
    empty()  返回是否为空
    clear()  清空
    front()/back()
    push_back()/pop_back()
    begin()/end()
    []
    支持比较运算,按字典序
pair<int, int>
    first, 第一个元素
    second, 第二个元素
    支持比较运算,以first为第一关键字,以second为第二关键字(字典序)
string,字符串
    size()/length()  返回字符串长度
    empty()
    clear()
    substr(起始下标,(子串长度))  返回子串
    c_str()  返回字符串所在字符数组的起始地址
queue, 队列
    size()
    empty()
    push()  向队尾插入一个元素
    front()  返回队头元素
    back()  返回队尾元素
    pop()  弹出队头元素
priority_queue, 优先队列,默认是大根堆
    size()
    empty()
    push()  插入一个元素
    top()  返回堆顶元素
    pop()  弹出堆顶元素
    定义成小根堆的方式:priority_queue<int, vector<int>, greater<int>> q;
stack, 栈
    size()
    empty()
    push()  向栈顶插入一个元素
    top()  返回栈顶元素
    pop()  弹出栈顶元素
deque, 双端队列
    size()
    empty()
    clear()
    front()/back()
    push_back()/pop_back()
    push_front()/pop_front()
    begin()/end()
    []
set, map, multiset, multimap, 基于平衡二叉树(红黑树),动态维护有序序列
    size()
    empty()
    clear()
    begin()/end()
    ++, -- 返回前驱和后继,时间复杂度 O(logn)
    set/multiset
        insert()  插入一个数
        find()  查找一个数
        count()  返回某一个数的个数
        erase()
            (1) 输入是一个数x,删除所有x   O(k + logn)
            (2) 输入一个迭代器,删除这个迭代器
        lower_bound()/upper_bound()
            lower_bound(x)  返回大于等于x的最小的数的迭代器
            upper_bound(x)  返回大于x的最小的数的迭代器
    map/multimap
        insert()  插入的数是一个pair
        erase()  输入的参数是pair或者迭代器
        find()
        []  注意multimap不支持此操作。 时间复杂度是 O(logn)
        lower_bound()/upper_bound()
unordered_set, unordered_map, unordered_multiset, unordered_multimap, 哈希表
    和上面类似,增删改查的时间复杂度是 O(1)
    不支持 lower_bound()/upper_bound(), 迭代器的++,--
bitset, 圧位
    bitset<10000> s;
    ~, &, |, ^
    >>, <<
    ==, !=
    []
    count()  返回有多少个1
    any()  判断是否至少有一个1
    none()  判断是否全为0
    set()  把所有位置成1
    set(k, v)  将第k位变成v
    reset()  把所有位变成0
    flip()  等价于~
    flip(k) 把第k位取反

相关文章
|
6月前
|
存储 C++ 索引
c++数据结构
c++数据结构
51 3
|
1月前
|
消息中间件 缓存 调度
常见的8种数据结构
常见的数据结构包括数组、链表、队列、栈、树、堆、哈希表和图。
45 4
|
5月前
|
存储 算法 调度
|
6月前
|
算法 C++ 开发者
【C/C++ 数据结构 】 连通图的基本了解
【C/C++ 数据结构 】 连通图的基本了解
89 0
|
存储 Java C++
总结数据结构-1
总结数据结构-1
41 0
|
存储 索引
【数据结构】树塔
【数据结构】树塔
153 0
uiu
|
存储 机器学习/深度学习 算法
我对八种常见数据结构的理解(一)
我对八种常见数据结构的理解(一)
uiu
131 0
我对八种常见数据结构的理解(一)
|
存储 Java 索引