[TJOI2019]甲苯先生和大中锋的字符串——后缀自动机+差分
题目链接:
对原串建后缀自动机并维护$parent$树上每个点的子树大小,显然子树大小为$k$的节点所代表的子串出现过$k$次,那么我们需要将$[len[fa[i]]+1,len[i]]$这一段区间的数目都$+1$,只需要差分即可,最后求前缀和并求出所有前缀和的最大值的位置即为答案。
#include<set> #include<map> #include<queue> #include<stack> #include<cmath> #include<cstdio> #include<vector> #include<bitset> #include<cstring> #include<iostream> #include<algorithm> using namespace std; int T; char ch[100010]; int n,k; int s[100010]; int last; int tr[200010][26]; int len[200010]; int pre[200010]; int head[200010]; int next[200010]; int to[200010]; int tot; int cnt; int size[200010]; void add(int x,int y) { next[++tot]=head[x]; head[x]=tot; to[tot]=y; } void dfs(int x) { for(int i=head[x];i;i=next[i]) { dfs(to[i]); size[x]+=size[to[i]]; } } void init() { memset(s,0,sizeof(s)); memset(tr,0,sizeof(tr)); memset(len,0,sizeof(len)); memset(pre,0,sizeof(pre)); memset(head,0,sizeof(head)); memset(size,0,sizeof(size)); tot=0; last=cnt=1; } void insert(int x) { int p=last; int np=++cnt; last=np; size[np]++; len[np]=len[p]+1; for(;p&&!tr[p][x];p=pre[p]) { tr[p][x]=np; } if(!p) { pre[np]=1; } else { int q=tr[p][x]; if(len[q]==len[p]+1) { pre[np]=q; } else { int nq=++cnt; len[nq]=len[p]+1; pre[nq]=pre[q]; memcpy(tr[nq],tr[q],sizeof(tr[q])); pre[np]=pre[q]=nq; for(;p&&tr[p][x]==q;p=pre[p]) { tr[p][x]=nq; } } } } void solve() { scanf("%s%d",ch+1,&k); n=strlen(ch+1); for(int i=1;i<=n;i++) { insert(ch[i]-'a'); } for(int i=2;i<=cnt;i++) { add(pre[i],i); } dfs(1); for(int i=1;i<=cnt;i++) { if(size[i]==k) { s[len[pre[i]]+1]++; s[len[i]+1]--; } } int sum=0; int mx=0; int ans=0; for(int i=1;i<=n;i++) { sum+=s[i]; if(sum>=mx) { mx=sum,ans=i; } } printf("%d\n",mx>0?ans:-1); } int main() { scanf("%d",&T); while(T--) { init(); solve(); } }