LGP3318题解
看完题后第一眼:是不是要 trie 子树和什么的东西啊。
第二眼:哦好像直接大力哈希就行了。
对于长的那边,把长度 ls+lt/2 的部分和剩下的部分做匹配(不应该叫匹配吧其实,反正就是循环之后可以对的上(?))。
然后把匹配位置求出来后对于每个匹配成功的位置,对剩下的位置做 Hash,然后丢个桶数一数就好了。
复杂度 \(O(n+m)\) 随便乱草。
#include<cstdio>
const int M=4000005,mod=1145141923,MOD=1000003;
int n,m,ls,lt,l1,l2,pw[M];long long ans;char*s[M],*t[M];int S[M];
inline void swap(char&a,char&b){
char c=a;a=b;b=c;
}
inline void swap(int&a,int&b){
int c=a;a=b;b=c;
}
struct HashTable{
int cnt,h[MOD];
struct Node{
int key,num,nx;
}t[M];
inline void Ins(const int&key){
int&H=h[key%MOD];for(int E=H;E;E=t[E].nx)if(t[E].key==key)return void(++t[E].num);
t[++cnt]=(Node){key,1,H};H=cnt;
}
inline int Qry(const int&key){
for(int E=h[key%MOD];E;E=t[E].nx)if(t[E].key==key)return t[E].num;return 0;
}
}Hash;
inline int hash(const int&L,const int&R){
return(S[R]+1ll*(mod-pw[R-L+1])*S[L-1])%mod;
}
signed main(){
scanf("%d%d%d%d",&n,&m,&ls,<);pw[0]=1;pw[1]=13331;for(int i=2;i<=ls||i<=lt;++i)pw[i]=13331ll*pw[i-1]%mod;
if(ls<lt){
for(int i=1;i<=n;++i)t[i]=new char[ls+1],scanf("%s",t[i]+1);
for(int i=1;i<=m;++i)s[i]=new char[lt+1],scanf("%s",s[i]+1);
for(int i=1;i<=n;++i)for(int j=1;(j<<1|1)<ls;++j)swap(s[i][j],s[i][ls-j+1]);
for(int i=1;i<=m;++i)for(int j=1;(j<<1|1)<lt;++j)swap(t[i][j],t[i][lt-j+1]);
swap(ls,lt);swap(n,m);
}
else{
for(int i=1;i<=n;++i)s[i]=new char[ls+1],scanf("%s",s[i]+1);
for(int i=1;i<=m;++i)t[i]=new char[lt+1],scanf("%s",t[i]+1);
}
l1=ls+lt>>1;l2=ls-l1;
for(int i=1;i<=m;++i){
int H(0);for(int k=1;k<=lt;++k)H=(13331ll*H+(t[i][k]^96))%mod;Hash.Ins(H);
}
for(int i=1;i<=n;++i){
for(int k=1;k<=ls;++k)S[k]=(13331ll*S[k-1]+(s[i][k]^96))%mod;
const int&H=hash(l1+1,ls);
for(int k=1;k<=l1;++k){
if(k+l2-1<=l1){
if(hash(k,k+l2-1)==H){
ans+=Hash.Qry((1ll*hash(k+l2,l1)*pw[k-1]+hash(1,k-1))%mod);
}
}
else{
if((1ll*hash(k,l1)*pw[l2-(l1-k+1)]+hash(1,l2-(l1-k+1)))%mod==H){
ans+=Hash.Qry(hash(l2-(l1-k+1),k));
}
}
}
}
printf("%lld",ans);
}