1 条题解
-
0
SAM一道很裸的题。。。
题意很明确,不再阐述。
做法很简单:找到所有出现次数为 的子串,然后统计。
怎么找到这些字符串呢?SAM 只能找出等价类啊。
注意 parent tree 的父亲节点的长度 +1 即该等价类中长度最短的字符串,那么若我们先通过拓扑排序求出每个等价类在原串中出现次次数 ,该等价类的所有字符串都出现了 次,那么就变为了一个序列操作题:
- 区间加
- 查询前缀和 而所有 操作都在 操作之后,我们可以用 的差分优秀地解决这个问题。
贴代码:
#include<cstring> #include<cstdio> const int M=1e5+5; int T,n,k,tot=1,lst=1,sum[M],id[M<<1],CB[M<<1],siz[M<<1];char s[M]; struct Node{ int chi[26]; int f,len; Node():f(0),len(0){memset(chi,0,104);} }SAM[M<<1]; inline void Insert(const int&s){ int q,p,nq,np; p=lst;np=lst=++tot; SAM[np].len=SAM[p].len+1;siz[np]=1; for(;p&&!SAM[p].chi[s];p=SAM[p].f)SAM[p].chi[s]=np; if(!p)SAM[np].f=1; else{ q=SAM[p].chi[s]; if(SAM[q].len==SAM[p].len+1)SAM[np].f=q; else{ SAM[nq=++tot]=SAM[q]; SAM[np].f=SAM[q].f=nq; SAM[nq].len=SAM[p].len+1; for(;p&&SAM[p].chi[s]==q;p=SAM[p].f)SAM[p].chi[s]=nq; } } } inline int Solve(){ register int i,u,ans=n+1; for(i=1;i<=tot;++i)++CB[SAM[i].len]; for(i=1;i<=tot;++i)CB[i]+=CB[i-1]; for(i=1;i<=tot;++i)id[CB[SAM[i].len]--]=i; for(i=tot;i>=1;--i){ u=id[i]; siz[SAM[u].f]+=siz[u]; if(siz[u]==k)++sum[SAM[u].len],--sum[SAM[SAM[u].f].len]; } for(i=n;i>=1;--i){ sum[i]+=sum[i+1]; if(sum[i]>sum[ans])ans=i; } ans=sum[ans]?ans:-1; while(tot)id[tot]=CB[tot]=siz[tot]=0,SAM[tot--]=Node(); for(i=1;i<=n;++i)sum[i]=0; tot=lst=1; return ans; } signed main(){ register int i; scanf("%d",&T); while(T--){ scanf("%s%d",s,&k);n=strlen(s); for(i=0;i<n;++i)Insert(s[i]-97),s[i]=0; printf("%d\n",Solve()); } }
- 1
信息
- ID
- 10514
- 时间
- 1000ms
- 内存
- 128MiB
- 难度
- 10
- 标签
- 递交数
- 2
- 已通过
- 1
- 上传者