BZOJ 3796 Mushroom追妹纸 哈希+二分(+KMP)

时间:2023-03-09 02:10:13
BZOJ 3796 Mushroom追妹纸 哈希+二分(+KMP)

先把两个串能匹配模式串的位置找出来,然后标记为$1$(标记在开头或末尾都行),然后对标记数组求一个前缀和,这样可以快速查到区间内是否有完整的一个模式串。

然后二分子串(答案)的长度,每次把长度为$md$的串扔到哈希表里,查一波匹不匹配。

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#define ull unsigned long long
#define ll long long
#define R register int
using namespace std;
const int M=,N=,B=;
namespace HASH {
const int P=;
int fir[P],nxt[M],cnt; ull vl[M];
inline void clear() {memset(fir,,sizeof(fir)),memset(nxt,,sizeof(int)*(cnt+)),memset(vl,,sizeof(ull)*(cnt+)),cnt=;}
inline void ins(ull x) {
R st=x%P; for(R i=fir[st];i;i=nxt[i]) if(vl[i]==x) return ;
vl[++cnt]=x,nxt[cnt]=fir[st],fir[st]=cnt;
}
inline int find(ull x) {R st=x%P; for(R i=fir[st];i;i=nxt[i]) if(vl[i]==x) return ; return ;}
} int l1,l2,ls,nxt[N/],c1[N],c2[N];
char s1[N],s2[N],s[N/];
ull h1[N],h2[N],p[N];
inline void PRE() {
for(R i=,j=;i<=ls;++i) {
while(j&&(s[i]!=s[j+])) j=nxt[j];
if(s[i]==s[j+]) ++j; nxt[i]=j;
}
}
inline void calc() {
for(R i=,j=;i<=l1;++i) {
while(j&&(j==ls||s1[i]!=s[j+])) j=nxt[j];
if(s1[i]==s[j+]) ++j;
if(j==ls) ++c1[i];
} for(R i=;i<=l1;++i) c1[i]+=c1[i-];
for(R i=,j=;i<=l2;++i) {
while(j&&(j==ls||s2[i]!=s[j+])) j=nxt[j];
if(s2[i]==s[j+]) ++j;
if(j==ls) ++c2[i];
} for(R i=;i<=l2;++i) c2[i]+=c2[i-];
}
inline bool ck(int md) {
for(R i=md;i<=l1;++i) if(md<ls||c1[i]-c1[i-md+ls-]==) HASH::ins(h1[i]-h1[i-md]*p[md]);
for(R i=md;i<=l2;++i) if(md<ls||c2[i]-c2[i-md+ls-]==) if(HASH::find(h2[i]-h2[i-md]*p[md])) return true;
HASH::clear(); return false;
}
signed main() {
scanf("%s%s%s",s1+,s2+,s+);
l1=strlen(s1+),l2=strlen(s2+),ls=strlen(s+);
PRE(),calc(); p[]=; for(R i=;i<=;++i) p[i]=p[i-]*B;
for(R i=;i<=l1;++i) h1[i]=h1[i-]*B+s1[i];
for(R i=;i<=l2;++i) h2[i]=h2[i-]*B+s2[i];
R l=,r=min(l1,l2)+; while(l<r) {R md=l+r>>; if(ck(md)) l=md+; else r=md;}
printf("%d\n",l-);

2019.06.12