[TJOI2019]甲苯先生和大中锋的字符串——后缀自动机+差分

题目链接:

[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();
    }
}
posted @ 2019-05-09 00:17  The_Virtuoso  阅读(255)  评论(0编辑  收藏  举报