Description
link
给定一些模板串和一些询问,每个询问以字符串的形式给出
对于每个询问给出的字符串,可以将其进行分割,使得其子串是某个模板串的子串
同时有一个标量 \(p\) 为分割方案中最小的 子串 的长度
求所有方案中的 \(p_{max}\)
Solution
求子串的方法就是后缀自动机,然后这题先对所有模板串建广义 \(sam\)
这个 \(p\) 是可以二分的,然后把问题转成判定性问题
设 \(f_i\) 为当前询问前 \(i\) 个字符能匹配的最大长度,然后有
\[f_i=\max( \max\limits_{j=i-match_i}^{i-p}f_j+i-j,f_{i-1}) \]
其中 \(match_i\) 是当前字符在广义 \(sam\) 的最长匹配长度
可以把询问放到上面跑一下求出来
如果最后 \(f_l\ge p\times0.9\) 就表示当下的 \(p\) 满足
然后我们发现这个方程并不能满足我们的时间需求
看看式子,有种斜率优化的冲动
打个表发现两边都满足不降……
(这里证明?看看nofind的博客吧)
然后直接单调队列优化这个过程即可
Code
#include
using namespace std;
#define int long long
namespace yspm{
inline int read()
{
int res=0,f=1; char k;
while(!isdigit(k=getchar())) if(k=='-') f=-1;
while(isdigit(k)) res=res*10+k-'0',k=getchar();
return res*f;
}
const int N=1.1e6+10;
int fa[N<<1],ch[N<<1][2],len[N<<1],tot=1,las=1,n,m;
inline void copy(int x,int y)
{
for(int i=0;i<2;++i) ch[x][i]=ch[y][i];
return ;
}
inline void extend(int x)
{
int tmp=las,np=las=++tot; len[np]=len[tmp]+1;
while(tmp&&!ch[tmp][x]) ch[tmp][x]=np,tmp=fa[tmp];
if(!tmp) return fa[np]=1,void();
int q=ch[tmp][x];
if(len[q]==len[tmp]+1) return fa[np]=q,void();
int clone=++tot; fa[clone]=fa[q]; fa[q]=fa[np]=clone; copy(clone,q);
len[clone]=len[tmp]+1;
while(tmp&&ch[tmp][x]==q) ch[tmp][x]=clone,tmp=fa[tmp];
return ;
}
char s[N];
int maxx[N],q[N],h,t,l,f[N],T;
inline void prework()
{
int res=0,now=1; l=strlen(s+1);
for(int i=1;i<=l;++i)
{
int t=s[i]-'0';
while(now&&!ch[now][t]) now=fa[now],res=len[now];
if(now) now=ch[now][t],res++;
else now=1,res=0;
maxx[i]=res;
} return ;
}
inline bool check(int x)
{
h=1; t=0; q[1]=0;
for(int i=1;i=l*9;
}
signed main()
{
T=read(); m=read();
for(int i=1,tl;i<=m;++i)
{
scanf("%s",s+1); tl=strlen(s+1); las=1;
for(int j=1;j<=tl;++j) extend(s[j]-'0');
}
while(T--)
{
scanf("%s",s+1); prework();
int l=1,r=strlen(s+1),ans=0;
while(l<=r)
{
int mid=(l+r)>>1;
if(check(mid)) ans=mid,l=mid+1;
else r=mid-1;
}printf("%lld\n",ans);
}
return 0;
}
}
signed main(){return yspm::main();}