![【BZOJ】2099: [Usaco2010 Dec]Letter 恐吓信 【BZOJ】2099: [Usaco2010 Dec]Letter 恐吓信](https://image.shishitao.com:8440/aHR0cHM6Ly9ia3FzaW1nLmlrYWZhbi5jb20vdXBsb2FkL2NoYXRncHQtcy5wbmc%2FIQ%3D%3D.png?!?w=700&webp=1)
【题意】给定长度为n和m的两个字符串S和T,要求在字符串S中取出若干段拼成T(可重复取),求最小段数,n,m<=50000。
【算法】后缀自动机 || 后缀数组
【题解】对串S建SAM,然后在上面尽可能地匹配T,匹配几次得到T就是答案。
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cctype>
using namespace std;
const int maxn=;
struct tree{int len,fa,t[];}t[maxn*];
int tot,last,n,m,b[maxn];
void insert(int c){
int np=++tot;
t[np].len=t[last].len+;
int x=last;
while(x&&!t[x].t[c])t[x].t[c]=np,x=t[x].fa;
last=np;
if(!x)t[np].fa=;else{
int y=t[x].t[c];
if(t[y].len==t[x].len+)t[np].fa=y;else{
int nq=++tot;
t[nq]=t[y];
t[nq].len=t[x].len+;
t[nq].fa=t[y].fa;t[y].fa=t[np].fa=nq;
while(x&&t[x].t[c]==y)t[x].t[c]=nq,x=t[x].fa;
}
}
}
int main(){
scanf("%d%d",&n,&m);
tot=last=;
for(int i=;i<=n;i++){
char c=getchar();
while(c<'A'||c>'Z')c=getchar();
insert(c-'A');
}
for(int i=;i<=m;i++){
char c=getchar();
while(c<'A'||c>'Z')c=getchar();
b[i]=c-'A';
}
int len=,ans=;
while(len<=m){
int now=;
while(len<=m&&t[now].t[b[len]])now=t[now].t[b[len++]];
ans++;
}
printf("%d",ans);
return ;
}