Description
SD有一名神犇叫做Oxer,他觉得字符串的题目都太水了,于是便出了一道题来虐蒟蒻yts1999。
他给出了一个字符串T,字符串T中有且仅有4种字符 'A', 'B', 'C', 'D'。现在他要求蒟蒻yts1999构造一个新的字符串S,构造的方法是:进行多次操作,每一次操作选择T的一个子串,将其加入S的末尾。
对于一个可构造出的字符串S,可能有多种构造方案,Oxer定义构造字符串S所需的操作次数为所有构造方案中操作次数的最小值。
Oxer想知道对于给定的正整数N和字符串T,他所能构造出的所有长度为N的字符串S中,构造所需的操作次数最大的字符串的操作次数。
蒟蒻yts1999当然不会做了,于是向你求助。
Input
第一行包含一个整数N,表示要构造的字符串长度。
第二行包含一个字符串T,T的意义如题所述。
Output
输出文件包含一行,一个整数,为你所求出的最大的操作次数。
题解: 有一个性质:操作次数越多,所能构造出来的最短串一定越长.
可以二分这个操作次数 $mid$,如果操作 $mid$ 次下所构造出来的最短的串的长度也大于 $n$,那么说明 $[mid+1,r]$ 所能构造出来的最短串的长度也大于 $n$,那么我们就可以把规模缩短到 $[l,mid-1]$
考虑二分出一个答案后如何检验
令 $f_{i,j}$ 表示以 $i$ 字符开头的单词后面可以接以 $j$ 字符开头的单词且$i$ 开头单词加上 $j$ 后还不是 $T$ 的子串的最短长度
这个可以在后缀自动机上求
相当于要求一个 以 $i$ 开头的子串,且子串的末尾还没有 $j$ 这条边
反向更新一下即可
求出 $f$ 数组后,考虑 $g_{i,j,k}$ 表示以 $i$ 开头,后面加 $j$,一共操作了 $k$ 次的最短长度,则 $g_{i,j,k}=g_{i,m,k-1}+g_{m,j,1}$ 我们发现这个东西可以用矩阵乘法来加速,来一遍矩阵快速幂即可
可以二分这个操作次数 $mid$,如果操作 $mid$ 次下所构造出来的最短的串的长度也大于 $n$,那么说明 $[mid+1,r]$ 所能构造出来的最短串的长度也大于 $n$,那么我们就可以把规模缩短到 $[l,mid-1]$
考虑二分出一个答案后如何检验
令 $f_{i,j}$ 表示以 $i$ 字符开头的单词后面可以接以 $j$ 字符开头的单词且$i$ 开头单词加上 $j$ 后还不是 $T$ 的子串的最短长度
这个可以在后缀自动机上求
相当于要求一个 以 $i$ 开头的子串,且子串的末尾还没有 $j$ 这条边
反向更新一下即可
求出 $f$ 数组后,考虑 $g_{i,j,k}$ 表示以 $i$ 开头,后面加 $j$,一共操作了 $k$ 次的最短长度,则 $g_{i,j,k}=g_{i,m,k-1}+g_{m,j,1}$ 我们发现这个东西可以用矩阵乘法来加速,来一遍矩阵快速幂即可
#include<bits/stdc++.h> #define maxn 300000 #define inf 2000000000000000000 #define ll long long #define setIO(s) freopen(s".in","r",stdin) using namespace std; ll n; char str[maxn]; namespace SAM { int last,tot; int trans[maxn][6], f[maxn], len[maxn], c[maxn], rk[maxn], F[maxn][7]; void init() { last=tot=1; } void extend(int c) { int np=++tot,p=last; len[np]=len[p]+1, last=np; while(p&&!trans[p][c]) trans[p][c]=np,p=f[p]; if(!p) f[np]=1; else { int q=trans[p][c]; if(len[q]==len[p]+1) f[np]=q; else { int nq=++tot; len[nq]=len[p]+1; memcpy(trans[nq], trans[q], sizeof(trans[q])); f[nq]=f[q], f[np]=f[q]=nq; while(p&&trans[p][c]==q) trans[p][c]=nq,p=f[p]; } } } void prepare() { memset(F,0x3f,sizeof(F)); for(int i=1;i<=tot;++i) ++c[len[i]]; for(int i=1;i<=tot;++i) c[i]+=c[i-1]; for(int i=1;i<=tot;++i) rk[c[len[i]]--]=i; for(int i=tot;i>=1;--i) { int o=rk[i]; for(int j=0;j<4;++j) { if(!trans[o][j]) F[o][j]=1; for(int k=0;k<4;++k) F[o][j]=min(F[o][j], F[trans[o][k]][j]+1); } } } }; struct matrix { ll mat[4][4]; void init(ll key) { for(int i=0;i<4;++i) for(int j=0;j<4;++j) mat[i][j]=key; } }; matrix operator*(matrix a,matrix b) { matrix c; c.init(inf); for(int i=0;i<4;++i) for(int j=0;j<4;++j) for(int k=0;k<4;++k) c.mat[i][j]=min(c.mat[i][j], a.mat[i][k] + b.mat[k][j]); return c; } matrix operator^(matrix base,ll k) { matrix tmp; tmp.init(0); while(k){ if(k&1) tmp=tmp*base; base=base*base,k>>=1; } return tmp; } bool check(ll mid) { matrix s; for(int i=0;i<4;++i) for(int j=0;j<4;++j) s.mat[i][j]=SAM::F[SAM::trans[1][i]][j]; s=s^mid; ll re=inf; for(int i=0;i<4;++i) for(int j=0;j<4;++j) re=min(re, s.mat[i][j]); return re >= n; } int main() { // setIO("input"); int i,_len; scanf("%lld%s",&n,str+1); _len=strlen(str+1); SAM::init(); for(i=1;i<=_len;++i) { SAM::extend(str[i]-'A'); } SAM::prepare(); ll l=1, r=n, mid, ans; while(l<=r){ mid=(l+r)>>1; if(check(mid)) ans=mid, r=mid-1; else l=mid+1; } printf("%lld ",ans); return 0; }