// s[]是长文本,p[]是模式串,n是s的长度,m是p的长度
求模式串的Next数组:
for(int i =2, j =0; i <= m; i ++){while(j && p[i]!= p[j +1]) j = ne[j];if(p[i]== p[j +1]) j ++;
ne[i]= j;}// 匹配for(int i =1, j =0; i <= n; i ++){while(j && s[i]!= p[j +1]) j = ne[j];if(s[i]== p[j +1]) j ++;if(j == m){
j = ne[j];// 匹配成功后的逻辑}}
Trie树
int son[N][26], cnt[N], idx;// 0号点既是根节点,又是空节点// son[][]存储树中每个节点的子节点// cnt[]存储以每个节点结尾的单词数量// 插入一个字符串voidinsert(char*str){int p =0;for(int i =0; str[i]; i ++){int u = str[i]-'a';if(!son[p][u]) son[p][u]=++ idx;
p = son[p][u];}
cnt[p]++;}// 查询字符串出现的次数intquery(char*str){int p =0;for(int i =0; str[i]; i ++){int u = str[i]-'a';if(!son[p][u])return0;
p = son[p][u];}return cnt[p];}
并查集
(1)朴素并查集:
int p[N];//存储每个点的祖宗节点// 返回x的祖宗节点intfind(int x){if(p[x]!= x) p[x]=find(p[x]);return p[x];}// 初始化,假定节点编号是1~nfor(int i =1; i <= n; i ++) p[i]= i;// 合并a和b所在的两个集合:
p[find(a)]=find(b);(2)维护size的并查集:
int p[N], size[N];//p[]存储每个点的祖宗节点, size[]只有祖宗节点的有意义,表示祖宗节点所在集合中的点的数量// 返回x的祖宗节点intfind(int x){if(p[x]!= x) p[x]=find(p[x]);return p[x];}// 初始化,假定节点编号是1~nfor(int i =1; i <= n; i ++){
p[i]= i;
size[i]=1;}// 合并a和b所在的两个集合:
size[find(b)]+= size[find(a)];
p[find(a)]=find(b);(3)维护到祖宗节点距离的并查集:
int p[N], d[N];//p[]存储每个点的祖宗节点, d[x]存储x到p[x]的距离// 返回x的祖宗节点intfind(int x){if(p[x]!= x){int u =find(p[x]);
d[x]+= d[p[x]];
p[x]= u;}return p[x];}// 初始化,假定节点编号是1~nfor(int i =1; i <= n; i ++){
p[i]= i;
d[i]=0;}// 合并a和b所在的两个集合:
p[find(a)]=find(b);
d[find(a)]= distance;// 根据具体问题,初始化find(a)的偏移量
堆
// h[N]存储堆中的值, h[1]是堆顶,x的左儿子是2x, 右儿子是2x + 1// ph[k]存储第k个插入的点在堆中的位置// hp[k]存储堆中下标是k的点是第几个插入的int h[N], ph[N], hp[N], size;// 交换两个点,及其映射关系voidheap_swap(int a,int b){swap(ph[hp[a]],ph[hp[b]]);swap(hp[a], hp[b]);swap(h[a], h[b]);}voiddown(int u){int t = u;if(u *2<= size && h[u *2]< h[t]) t = u *2;if(u *2+1<= size && h[u *2+1]< h[t]) t = u *2+1;if(u != t){heap_swap(u, t);down(t);}}voidup(int u){while(u /2&& h[u]< h[u /2]){heap_swap(u, u /2);
u >>=1;}}// O(n)建堆for(int i = n /2; i; i --)down(i);
一般哈希
(1) 拉链法
int h[N], e[N], ne[N], idx;// 向哈希表中插入一个数voidinsert(int x){int k =(x % N + N)% N;
e[idx]= x;
ne[idx]= h[k];
h[k]= idx ++;}// 在哈希表中查询某个数是否存在boolfind(int x){int k =(x % N + N)% N;for(int i = h[k]; i !=-1; i = ne[i])if(e[i]== x)returntrue;returnfalse;}(2) 开放寻址法
int h[N];// 如果x在哈希表中,返回x的下标;如果x不在哈希表中,返回x应该插入的位置intfind(int x){int t =(x % N + N)% N;while(h[t]!= null && h[t]!= x){
t ++;if(t == N) t =0;}return t;}
字符串哈希
核心思想:将字符串看成P进制数,P的经验值是131或13331,取这两个值的冲突概率低
小技巧:取模的数用2^64,这样直接用unsignedlonglong存储,溢出的结果就是取模的结果
typedefunsignedlonglong 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];}