最小表示法,字符串哈希
最小表示法,字符串哈希
最小表示法
给出字符串 \(S\) ,在其所有的循环同构串中选择字典序最小的一个串
循环同构:当字符串 \(S\) 中可以选择一个位置 \(i\) ,满足 \(S_{i,n}+S_{1,i-1}=T\) 那么记 \(S,T\) 循环同构
可以利用类似双指针的办法解决
- 构造字符串 \(\hat{S}=S+S\) ,设立两个指针 \(u,v\)
例如: \(S=b\ c\ a\ d\ a\ b\ d\ d\) ,\(\hat{S}=b\ c\ a\ d\ a\ b\ d\ d\ b\ c\ a\ d\ a\ b\ d\ d\)
一开始 \(u=1,v=2\) 设前 \(k\) 个字符相同,当 \(\hat{S}_{u+k}\ne \hat{S}_{v+k}\) 时,指针可以移动
- \(\hat{S}_{u+k}> \hat{S}_{v+k}\) 说明以 \(u\) 为开始的循环同构串字典序大于以 \(v\) 开始的,那么移动 \(u=u+k+1\)
- \(\hat{S}_{u+k}< \hat{S}_{v+k}\) 说明以 \(v\) 为开始的循环同构串字典序大于以 \(u\) 开始的,那么移动 \(v=v+k+1\)
因为 \(\hat{S}_U\) 与 \(\hat{S}_V\) 前 \(k\) 个字符相同,说明字典序相同,那么下一次判断可以跳过这段重复的区间
- 如果 \(\hat{S}_{u+k}= \hat{S}_{v+k}\),说明可以增加 \(k\) 区间长度,即 \(k+1\)
string s;
cin>>s;
int n=s.size();
s="0"+s+s;//0占位符,规范下标从1开始
int u=1,v=2,k=0;
while (u<=n&&v<=n){
for (k=0;k<n&&s[u+k]==s[v+k];k++);
if (s[u+k]>s[v+k]) u+=k+1;
else v+=k+1;//相等的情况前面循环已排除
if (u==v) u++;
}
最后的答案 \(ans={\rm{min}}(u,v)\) ,这是因为在
if (s[u+k]>s[v+k]) u+=k+1;
else v+=k+1;
过程中,如果 \(u,v\) 向后移动超过了 \(n\) 的范围就会立刻终止循环,又因为 \([n+1,2n]\) 的区间与 \([1,n]\) 区间完全一致
那么在 \(n\) 后位置上确定的循环同构串已经在 \(n\) 前判断过,所以输出 \(u,v\) 最小者即可
字符串哈希
给定许多字符串,判断其中是否存在重复的串
如果暴力判断会浪费许多时间在字符串的匹配上,这时候可以利用哈希函数将这个字符串映射为一个数值
映射后不同的数值对应的原字符串一定不相同,相同的数值对应的原字符串不一定相同
显然,我们肯定是理想让不同字符串映射后的数值都不相同,(相同称为哈希冲突)
首先是字符串常用的哈希方式:模数哈希
每个字符串的每个字符都对应一个 ASCII 码,天然可以映射为一个数字,那么对于长为 \(n\) 的字符串 \(S\) ,定义它的模数哈希函数为
其中的 \(B\) 表示 \(B\) 进制,将整个字符串视为一个 \(B\) 进制的数,整个多项式对 \(M\) 取模
一般的,\(B\) 常取 \(131,13331,13131\) ,\(M\) 常取 \(2^{32}-1=212370440130137957,10^9+7,19260817\)
const ULL HashBase=13331;
const ULL HashMod=212370440130137957;
ULL hash(string s){
int len=s.size();
ULL res=0;
for (int i=0;i<len;i++)
res=(res*HashBase+(ULL)s[i])%HashMod;
return res;
}
估测哈希冲突的概率函数计算:哈希空间 \(M\) 下插入 \(k\) 个字符串的冲突概率,\(M\) 与模数定义相同
但是,这样的计算出来的哈希冲突概率仍然较高,可能会被卡
所以可以利用双值哈希的操作,将哈希冲突的概率降低到
即采用两个不同的哈希函数映射,当且仅当两个字符串的两个哈希值完全相同时才视作相同
const ULL HashBase=13331;
const ULL HashMod_1=212370440130137957;
const ULL HashMod_2=1e9+7;
ULL hash_1(string s){
int len=s.size();
ULL res=0;
for (int i=0;i<len;i++)
res=(res*HashBase+(ULL)s[i])%HashMod_1;
return res;
}
ULL hash_2(string s){
int len=s.size();
ULL res=0;
for (int i=0;i<len;i++)
res=(res*HashBase+(ULL)s[i])%HashMod_2;
return res;
}

浙公网安备 33010602011771号