【笔记】震惊!世上最接地气的字符串浅谈(HASH+KMP)
震惊!世上最接地气的字符串浅谈(HASH+KMP)
笔者过于垃圾,肯定会有些错的地方,欢迎各位巨佬指正,感激不尽!
引用:LYD的蓝书,一本通,DFC的讲稿,网上各路巨佬
Luguo id: 章鱼那个哥, uid : 87075
Hash
哈希算是除字符串的题,别的什么题用处最多的一种东西了。
干啥子用的:
怎么说呢,就像是给出一个字符串,我们把他化成一个数字。
这样可以实现快速查询两个字符串是否相同,需要 \(O(N)\) 预处理。
咋的写:
把这个字符串转换成一个BASE进制数,并对一个模数取模,最好这个模数是一个大质数,这样对哈希冲突的影响小一点,当然有些时候是合数反而更好。
代码:
inline int Hash(char s[],int p){
int sum=0;
for(int i=0;i<(int)strlen(s);++i)
sum=(sum*BASE+(int)s[i])%P;
return sum%p;
}
小拓展:
双哈希:
这不是对哈希值哈希,对哈希值哈希会造成更大程度的信息损失。
是对两个BASE,两个模数分别做哈希。
树哈希:
这个分好多种,有对树形哈希的,有对子树哈希的。
来看一道NOIP原题:
发现这题其实就是这个节点的两个子树的左序遍历(Lson,now,Rson)和右序遍历(Rson,now,Lson)相同时就可以更新答案了,那么我们其实就是要对这棵树的形状哈希,那么就可以想到一种办法:
左儿子,当前点,右儿子附加不同的BASE。
每次更新就可以按照左序右序更新,最后判断按照左序更新的哈希值和按照右序的哈希值是否一样就行了
一些应用:
-
可以在 \(O(NlogN)\) 时间内求出一个回文串,可以枚举中心,二分长度,哈希判断值
-
可以字符串匹配
-
可以瞎搞(可以参考NOIP-2014解方程)
当然还有后缀排序啥子的,但笔者不会,然而也莫得用到。。。
KMP
干啥子用的:
\(O(N+M)\) 匹配文本串中模式串出现的次数和位置等。
怎么做:
引入nxt[i]表示模式串长度为 \(i\) 时前后缀字符串相同的最长字符。
红色为已匹配成功的
那么接下来我们发现上面的已匹配的后两个是和下面的后两个是匹配的,那么我们不必把模式串指针移动回首位,只需要移动到nxt[失配字符的前一个],后文中,这个"失配字符的前一个"称作len。
这样每次失配就不会浪费时间从头匹配。
nxt可以自己匹配自己求出来。
代码
#include<bits/stdc++.h>
using namespace std;
int n,m,len;
const int N=1e6+5;
char a[N],b[N];
int nxt[N];
int main(){
int ans=0;
scanf("%s%s",a+1,b+1);
n=strlen(a+1);
m=strlen(b+1);
for(int i=2;i<=m;++i){
while(len && b[len+1]!=b[i]) len=nxt[len];
if(b[len+1]==b[i]) len++;
nxt[i]=len;
}
len=0;
for(int i=1;i<=n;++i){
while(len && b[len+1]!=a[i]) len=nxt[len];
if(b[len+1]==a[i]) len++;
if(len==m)
ans++,len=nxt[len];
}
printf("%d\n",ans);
for(int i=1;i<=m;++i) printf("%d ",nxt[i]);
putchar('\n');
return 0;
}
小拓展:
好像莫得,nxt数组倒有些别的用处。
小应用:
可以 \(O(N)\) 求最小循环节长度。考虑如果循环节长度为ans,
那么肯定会有 n-ans=nxt[n]
此篇完结,撒花~~
下一篇——Trie树和Manacher