[TJOI2019]甲苯先生和大中鋒的字符串——後綴自動機+差分
阿新 • • 發佈:2019-05-09
mem 後綴 loj == cpp sta using 中鋒 ace
題目鏈接:
[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(); } }
[TJOI2019]甲苯先生和大中鋒的字符串——後綴自動機+差分