哈希是个神奇的东西,因为从来没有老师讲而默认我们需要会?咕咕咕
字符串哈希
模板略。
回文字串
分别求哈希前缀和以及哈希后缀和,枚举每一个位置作为回文串中点,对回文串长度分奇偶讨论一下,分别二分长度即可。
#include<bits/stdc++.h>
#define ull unsigned long long
#define ll long long
#define ff(i,s,e) for(int i(s);i<=(e);++i)
using namespace std;
inline int read(){
int x=0,f=1;
char ch=getchar();
while(ch>'9'||ch<'0'){if(ch=='-') f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
return x*f;
}
const int N=1e6+5;
const int base=131,mod=1e9+7;
char s[N];
ull a[N],b[N],mi[N];
inline int solve(){
int n=strlen(s+1);
memset(a,0,sizeof(a));
memset(b,0,sizeof(b));
ff(i,1,n) a[i]=(a[i-1]*base+s[i])%mod;
for(int i=n;i;--i) b[i]=(b[i+1]*base+s[i])%mod;
int ans=1;
ff(i,2,n-1){
int l,r,res;
if(s[i]==s[i+1]){
l=1,r=min(i,n-i);
while(l<=r){
int mid=l+r>>1;
ull x=(a[i]-a[i-mid]*mi[mid]%mod+mod)%mod,y=(b[i+1]-b[i+mid+1]*mi[mid]%mod+mod)%mod;
if(x==y) l=mid+1,res=mid;
else r=mid-1;
}
ans=max(ans,res<<1);
}
l=1,r=min(i-1,n-i),res=0;
while(l<=r){
int mid=l+r>>1;
ull x=(a[i-1]-a[i-mid-1]*mi[mid]%mod+mod)%mod,y=(b[i+1]-b[i+mid+1]*mi[mid]%mod+mod)%mod;
if(x==y) l=mid+1,res=mid;
else r=mid-1;
}
ans=max(ans,res<<1|1);
}
return ans;
}
signed main(){
mi[0]=1;
ff(i,1,(int)1e6) mi[i]=mi[i-1]*base%mod;
for(int t=1;;++t){
scanf("%s",s+1);
if(s[1]=='E') return 0;
printf("Case %d: %d\n",t,solve());
}
}
对称正方形
二维哈希模板,存一个原矩阵,一个上下颠倒矩阵,一个左右颠倒矩阵,分别哈希预处理。然后枚举对称正方形中心点,分正方形边长分奇偶分别二分求判断是否对称即可。二维哈希查询也一个容斥就搞出来了。时间复杂度 O ( n 2 l o g n ) O(n^2logn) O(n2logn)。
很简单是吧,为什么连着写了两个晚上才写过呢,因为忘了矩阵颠倒之后原矩阵的 ( x , y ) (x,y) (x,y) 位置也变了啊啊啊啊我是什么制杖
#include<cstdio>
#include<iostream>
#include<algorithm>
#define ull unsigned long long
#define ll long long
#define ff(i,s,e) for(int i(s);i<=(e);++i)
using namespace std;
inline int read(){
int x=0,f=1;
char ch=getchar();
while(ch>'9'||ch<'0'){if(ch=='-') f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
return x*f;
}
const int basex=131,basey=313,N=1005;
int n,m;
ull a[N][N],b[N][N],c[N][N],mix[N],miy[N];
inline void ha(){
mix[0]=miy[0]=1;
ff(i,1,max(m,n)){
mix[i]=mix[i-1]*basex;
miy[i]=miy[i-1]*basey;
}
ff(i,1,n) ff(j,1,m){
a[i][j]=a[i][j-1]*basey+a[i][j];
b[i][j]=b[i][j-1]*basey+b[i][j];
c[i][j]=c[i][j-1]*basey+c[i][j];
}
ff(i,1,n) ff(j,1,m){
a[i][j]=a[i-1][j]*basex+a[i][j];
b[i][j]=b[i-1][j]*basex+b[i][j];
c[i][j]=c[i-1][j]*basex+c[i][j];
}
}
inline bool check(int x1,int y1,int x2,int y2){
if(x1<1||y1<1||x2>n||y2>m) return 0;
ull x=a[x2][y2]-a[x2][y1-1]*miy[y2-y1+1]-a[x1-1][y2]*mix[x2-x1+1]+a[x1-1][y1-1]*mix[x2-x1+1]*miy[y2-y1+1];
int x11=n-x2+1,x22=n-x1+1;//一定不要忘!!!
ull y=b[x22][y2]-b[x22][y1-1]*miy[y2-y1+1]-b[x11-1][y2]*mix[x22-x11+1]+b[x11-1][y1-1]*mix[x22-x11+1]*miy[y2-y1+1];
int y11=m-y2+1,y22=m-y1+1;
ull z=c[x2][y22]-c[x2][y11-1]*miy[y22-y11+1]-c[x1-1][y22]*mix[x2-x1+1]+c[x1-1][y11-1]*mix[x2-x1+1]*miy[y22-y11+1];
return (x==y)&&(x==z);
}
signed main(){
n=read(),m=read();
ff(i,1,n) ff(j,1,m) a[i][j]=b[n-i+1][j]=c[i][m-j+1]=read();
ha();
int ans=0;
ff(i,1,n) ff(j,1,m){
int l=0,r=min(m,n),res=0;
while(l<=r){
int mid=l+r>>1;
if(check(i-mid,j-mid,i+mid,j+mid)) l=mid+1,res=mid;
else r=mid-1;
}
ans+=res+1,l=1,r=min(m,n),res=0;
while(l<=r){
int mid=l+r>>1;
if(check(i-mid+1,j-mid+1,i+mid,j+mid)) l=mid+1,res=mid;
else r=mid-1;
}
ans+=res;
}
printf("%d",ans);
return 0;
}
单词背诵
哈希存要背的单词,通过哈希比较看有几个单词出现在文章中,然后通过尺取法找最小长度即可。(为什么以前会以为尺取法是个很高深的东西啊/kk
要特判文章里没有单词的情况,第二个直接输出0。
子正方形
想到和T3类似的思路,先分别将两个矩阵哈希一遍,再在第一个矩阵里枚举每一个点作为正方形的中心位置,二分正方形边长,在第二个矩阵里寻找是否存在哈希值一样的正方形即可。时间复杂度 O ( n 4 l o g n ) O(n^4logn) O(n4logn)。
话说我自己口胡完拿计算器敲了一下这个复杂度发现是八位数,以为是 3 e 8 3e8 3e8 不可过,自我怀疑了一会发现八位数是 3 e 7 3e7 3e7?我什么时候才能学会数数/kk
#include<cstdio>
#include<iostream>
#include<algorithm>
#define ull unsigned long long
#define ll long long
#define ff(i,s,e) for(int i(s);i<=(e);++i)
using namespace std;
inline int read(){
int x=0,f=1;
char ch=getchar();
while(ch>'9'||ch<'0'){if(ch=='-') f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
return x*f;
}
const int N=55,basex=131,basey=313;
int n;
ull a[N][N],b[N][N],mix[N],miy[N];
inline void ha(){
mix[0]=miy[0]=1;
ff(i,1,n){
mix[i]=mix[i-1]*basex;
miy[i]=miy[i-1]*basey;
}
ff(i,1,n) ff(j,1,n){
a[i][j]=a[i][j-1]*basey+a[i][j];
b[i][j]=b[i][j-1]*basey+b[i][j];
}
ff(i,1,n) ff(j,1,n){
a[i][j]=a[i-1][j]*basex+a[i][j];
b[i][j]=b[i-1][j]*basex+b[i][j];
}
}
inline bool check(int x1,int y1,int x2,int y2){
if(x1<1||y1<1||x2>n||y2>n) return 0;
int len=x2-x1+1;
ull x=a[x2][y2]-a[x2][y1-1]*miy[y2-y1+1]-a[x1-1][y2]*mix[x2-x1+1]+a[x1-1][y1-1]*mix[x2-x1+1]*miy[y2-y1+1];
for(x1=1;x1<=n-len+1;++x1) for(y1=1;y1<=n-len+1;++y1){
x2=x1+len-1,y2=y1+len-1;
ull y=b[x2][y2]-b[x2][y1-1]*miy[y2-y1+1]-b[x1-1][y2]*mix[x2-x1+1]+b[x1-1][y1-1]*mix[x2-x1+1]*miy[y2-y1+1];
if(y==x) return 1;
}
return 0;
}
signed main(){
n=read();
ff(i,1,n) ff(j,1,n) a[i][j]=read();
ff(i,1,n) ff(j,1,n) b[i][j]=read();
ha();
int ans=0;
ff(i,1,n) ff(j,1,n){
int l=0,r=n,res=0;
while(l<=r){
int mid=l+r>>1;
if(check(i-mid,j-mid,i+mid,j+mid)) res=mid,l=mid+1;
else r=mid-1;
}
ans=max(ans,res<<1|1);
l=1,r=n,res=0;
while(l<=r){
int mid=l+r>>1;
if(check(i-mid+1,j-mid+1,i+mid,j+mid)) res=mid,l=mid+1;
else r=mid-1;
}
ans=max(ans,res<<1);
}
printf("%d",ans);
return 0;
}
特殊数列
哈希表模板略。
求好元素
O ( n 2 ) O(n^2) O(n2) 预处理出 a m + a n a_m+a_n am+an 的所有值并哈希,再 O ( n 2 ) O(n^2) O(n2)枚举 i i i 和 p p p,在哈希表中查找即可。
什么毒瘤卡常题啊,vector永远8000多msTLE,改链前就2000多?/kk
#include<bits/stdc++.h>
#define ull unsigned long long
#define ll long long
#define ff(i,s,e) for(int i(s);i<=(e);++i)
using namespace std;
inline int read(){
int x=0,f=1;
char ch=getchar();
while(ch>'9'||ch<'0'){if(ch=='-') f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
return x*f;
}
const int N=5005,mod=1e7+7;
int n,a[N];
int head[mod+2],cnt;
struct qwq{
int v,nxt;
}e[N*N];
inline void add(int u,int v){
e[++cnt]=qwq{v,head[u]},head[u]=cnt;
}
inline int ha(int x){
int res=x%mod+mod;
if(res>=mod) res-=mod;
return res;
}
inline bool check(int i,int val){
for(int j=head[i];j;j=e[j].nxt){
int v=e[j].v;
if(v==val) return 1;
}
return 0;
}
signed main(){
n=read();
ff(i,1,n) a[i]=read();
int ans=0;
ff(i,1,n){
int flag=0;
ff(j,1,i-1){
int val=a[i]-a[j],qwq=ha(val);
if(check(qwq,val)){flag=1;break;}
}
if(flag) ++ans;
ff(j,1,i){
int val=a[i]+a[j],qwq=ha(val);
if(!check(qwq,val)) add(qwq,val);
}
}
printf("%d",ans);
return 0;
}
上课点名
今天是哈希冲突人呢
取 b a s e = 131 base=131 base=131, m o d = 1 e 7 + 7 mod=1e7+7 mod=1e7+7 冲突成70pts,一顿魔改vector套pair最后没卡常卡着时限过了。
最大分离度
每个人对应一个点,直接有边的两个人之间边权为1,跑一遍floyd即可。
看到50的数据范围可以偷个懒写map也跑的1飞飞快/kx
多测清空啊qwq
回文分区
奇奇怪怪的乱搞,从两边往中间扫,发现前后有一段相同就计入答案,中间的位置要特判一下。
#include<bits/stdc++.h>
#define ull unsigned long long
#define ll long long
#define ff(i,s,e) for(int i(s);i<=(e);++i)
using namespace std;
inline int read(){
int x=0,f=1;
char ch=getchar();
while(ch>'9'||ch<'0'){if(ch=='-') f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
return x*f;
}
const int N=1e6+5,base=131;
char s[N];
ull a[N],mi[N];
inline void solve(){
scanf("%s",s+1);
int n=strlen(s+1);
ff(i,1,n) a[i]=a[i-1]*base+s[i];
int ans=0;
for(int st=1,ed=n;st<=ed;++st,--ed){
int len,pre=ed-st+1;
for(len=1;len<=pre;++len){
ull x=a[st+len-1]-a[st-1]*mi[len];
ull y=a[ed]-a[ed-len]*mi[len];
if(x==y) break;
}
ans+=2,st+=len-1,ed-=len-1;
if(st==ed-1) break;//特判一下正好的情况
if(st>=ed){--ans;break;}//中间一段不能形成回文的情况
}
printf("%d\n",ans);
}
signed main(){
mi[0]=1;
ff(i,1,(int)1e6) mi[i]=mi[i-1]*base;
int T=read();
while(T--) solve();
return 0;
}