字符串基础
字符串哈希(hash)
字符串哈希,其实就是类似于一个数组前缀和的一个东西,能够快速判断字符串的相等情况.
首先是用一个 unsigned 的自然溢出,因为模运算是非常慢的,所以要用到这么个自然溢出.
由于字符串是有序的,在判等的时候不能直接加数,否则是会被误认为相等的. 这时需要引入一个 base,一般选一个素数. 每次乘上这个素数,这样就是有序的了.
至于 base 取多少,多少其实都可以,只要不被卡就行 我一般用 131.
这是一个模板.
#define ull unsigned ll
#define base 131
char s[maxn];
ll hs[maxn];
inline void Hash()
{
for(rll i=1;i<=strlen(s+1);i++)
hs[i]=hs[i-1]*base+s[i];
}
怎么查询?
不能直接减去前面的,应该把要查询的这个串的前面串哈希的前缀和乘以一个 base 的要查询的字符串长度次方,再减(想一想,为什么?).
inline void gethash(rll l,rll r)
{
return hs[r]-hs[l-1]*ksm(base,r-l+1);// ksm指快速幂
}
KMP
kmp 一般用来处理询问的字符串在原串中出现了几次的问题.
它的重点就在于找到最长相同前后缀长度以及失配位置.
首先需要预处理出模板串的一个next数组,就是上面所说的那个模板串最长相同的前缀后缀长度.
在模板串和询问串匹配的时候,如果出现失配情况,由于前面的next数组已经处理出来了,所以每次失配的时候直接跳next就可以了.
实现:
// t为模板串,m是它的长度;s为匹配串,n是它的长度
for(rll i=1,j=0;i<=n;i++)
{
while(j&&(t[j+1]^s[i])) j=nxt[j];
if(t[j+1]==s[i]) j++;
if(j==m)
{
// i-j+1 即为所求的一个长度
j=nxt[j];
}
}
如何求 next 数组?
还是设 i 代表当前位置,j 代表 next 的位置. 令第一个位置的next值为 0. 不断移动 i 指针,如果 t[i]=t[j+1],那么它们一定是最长的相同前(后)缀(想一想,为什么?). 如果不相等,那就反复进行上面的操作直至它们相同或者next[i]=0(没有可匹配的了)为止.
for(rll i=2,j=0;i<=m;i++)
{
while(j&&(t[j+1]^t[i])) j=nxt[j];
if(t[j+1]==t[i]) j++;
nxt[i]=j;
}
trie
就是字典树.
它利用树形结构来确保复杂度. 插入和查找都是带 log 的.
具体做法就是每一个位置都是一层,那么每一次插入一个字母,就是往下了一层. 如果这一层已有这个字母,则跳过;否则,新建一个节点与上面连边. 根节点多设一个就行了.
在单词结尾的时候,打一个标记,证明这里是一个单词.
插入操作的实现:
ll trie[maxn][26],cnt;
bool tag[maxn];
inline void ins(rg char* s)
{
rll x=0;
for(rll i=1;i<=strlen(s+1);i++)
{
if(!trie[tot][s[i]^'0']) trie[tot][s[i]-'0']=++cnt;
x=trie[x][s[i]^'0'];
}
tag[x]=1;
}
查询也很简单,直接for一遍即可.
for(rll i=cnt;~i;i--)
{
for(rll j=0;j<26;j++)
{
// trie[i][j];
}
// tag[i];
}
--END--

浙公网安备 33010602011771号
我的博客: 𝟷𝙻𝚒𝚞
本文链接: https://www.cnblogs.com/1Liu/articles/16755777.html
版权声明: 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!