LOJ2823 三个朋友 ——查询字串的哈希值
概念
查询字串的hash值
我们所说的哈希通常都是进制哈希,因为它具有一些很方便的性质,例如,具有和前缀和类似的性质。
假设一个字符串的前缀哈希值记为 $h[i]$,进制为 $base$,那么显然 $h[i] = h[i-1] \times base + s[i]$.
记 $p[i]$ 为 $base$ 的 $i$ 次方,那么我们可以 $O(1)$ 得到一个字串的哈希值。
typedef unsigned long long ull;
ull get_hash(int l, int r) {
return h[r] - h[l - 1] * p[r - l + 1];
}
其中,乘以 $p[r-l+1]$ 相当于左移了 $r-l+1$ 位。
同样,我们可以 $O(1)$ 得到字串中删除一个字符后的hash值。
ull get_s(int l, int r, int x) {
return get_hash(l, x - 1) * p[r - x] + get_hash(x + 1, r);
}
例题
题意:给定一个字符串 $S$,先将字符串 $S$ 复制一次,得到字符串 $T$,然后在 $T$ 中插入一个字符,得到字符串 $U$。现给出字符串 $U$,要求重新构造出 $S$。
分析:
枚举每一个位置,剩下的应是两个相同的字符串,根据hash值判断一下。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const ull base = 233;
const int maxn = 2000000+10;
ull h[maxn], p[maxn];
char s[maxn];
int len;
void init()
{
ll ans = 0;
for(int i=0;i<len;i++)
{
ans = (ans*base) + (ull)s[i];
h[i] = ans;
}
p[0] = 1;
for(int i=1;i<=len;i++)
{
p[i] = p[i-1]*base;
}
}
inline ll gethash(int l,int r)
{
return h[r] - h[l-1]*p[r-l+1];
}
inline ll del(int l,int r,int x)
{
if(x<l||x>r)
return gethash(l,r);
return gethash(l,x-1)*p[r-x]+gethash(x+1,r);
}
inline int check(int x) //检查去掉第x位时是否合法
{
if(x<=len/2&&del(0,len-1,x)==del(0,len>>1,x)*p[len>>1]+del(0,len>>1,x))
return 1;
else if(x>len/2&&del(0,len-1,x)==del(0,(len>>1)-1,x)*p[len>>1]+del(0,(len>>1)-1,x))
return 1;
else
return 0;
}
int main()
{
scanf("%d",&len);
scanf("%s",s);
init();
int cnt = 0;
int ii = -1;
ll num;
for(int i=0;i<len;i++)
{
if(check(i))
{
cnt++;
if(ii==-1)
{
ii=i;
num = del(0,len-1,i);
}
else
{
if(del(0,len-1,ii)==del(0,len-1,i)) //可能是相同的串
cnt--;
}
}
if(cnt==2)
{
printf("NOT UNIQUE\n");
return 0;
}
}
if(cnt==1)
{
int m = 0;
for(int j=0;j<len;j++) //输出结果
{
if(j==ii)
continue;
printf("%c",s[j]);
m++;
if(m==(len-1)/2)
break;
}
printf("\n");
}
if(cnt==0)
{
printf("NOT POSSIBLE\n");
}
return 0;
}
参考链接:https://loj.ac/submission/576434
原编辑时间 posted @ 2019-08-19 09:34
个性签名:时间会解决一切