bzoj3277 串【解法二】

解法一【后缀数组+二分】见【这里】

n 个串的广义后缀自动机建出来,然后统计每个点所代表的串出现的次数。这个数值相当于它的fail子树中每个点代表的字符串的并集的大小。求出dfs序后,这个问题就相当于区间询问有多少个不同的数字,把询问按照右端点排序,每新加入一个数字就在它出现的上一个位置减掉,这样询问就是区间求和。求出来这个出现次数以后,如果它大于等于 k ,这个点的贡献就是val[u]val[fail[u]]【因为如果这个点有贡献,这个点的 fail 也一定有贡献】,否则没有贡献。然后再把这个贡献按照拓扑序更新一次,得到以每个点为结尾的子串的贡献,最后把每个串在自动机上dfs一遍就可以统计出答案了。

#include<cstdio>
#include<vector>
#include<cstring>
#include<algorithm>
using namespace std;
#define LL long long
const int maxn=300010;
char s[maxn];
int trie[maxn][27],trans[maxn][27],fail[maxn],val[maxn],num[maxn],
que[maxn],node[maxn],L[maxn],R[maxn],pre[maxn],sum[maxn],ord[maxn],
res[maxn],cnt[maxn],
n,m,tot=1,clo;
LL ans[maxn];
vector<int> v[maxn],id[maxn],son[maxn];
vector<pair<int,int> > qry[maxn];
int add(int last,int x)
{
    int p=last,np,q,nq;
    val[np=++tot]=val[last]+1;
    while (p&&!trans[p][x])
    {
        trans[p][x]=np;
        p=fail[p];
    }
    if (!p) fail[np]=1;
    else
    {
        q=trans[p][x];
        if (val[q]==val[p]+1) fail[np]=q;
        else
        {
            val[nq=++tot]=val[p]+1;
            fail[nq]=fail[q];
            fail[q]=fail[np]=nq;
            for (int i=1;i<=26;i++) trans[nq][i]=trans[q][i];
            while (p&&trans[p][x]==q)
            {
                trans[p][x]=nq;
                p=fail[p];
            }
        }
    }
    return np;
}
void dfs1(int x,int y)
{
    id[y]=v[x];
    for (int i=1;i<=26;i++)
        if (trie[x][i]) dfs1(trie[x][i],trans[y][i]);
}
void dfs2(int u)
{
    ord[L[u]=++clo]=u;
    for (vector<int>::iterator it=son[u].begin();it!=son[u].end();++it) dfs2(*it);
    R[u]=clo;
    qry[R[u]].push_back(make_pair(u,L[u]));
}
void dfs3(int x,int y)
{
    for (vector<int>::iterator it=v[x].begin();it!=v[x].end();++it)
        ans[*it]+=res[y];
    for (int i=1;i<=26;i++)
        if (trie[x][i]) dfs3(trie[x][i],trans[y][i]);
}
void dec(int k)
{
    for (;k<=clo;k+=k&-k) sum[k]--;
}
void inc(int k)
{
    for (;k<=clo;k+=k&-k) sum[k]++;
}
int query(int k)
{
    int ret=0;
    for (;k;k-=k&-k) ret+=sum[k];
    return ret;
}
int main()
{
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    int t1=0,l,p,hd,tl,u,mx=0;
    scanf("%d%d",&n,&m);
    for (int i=1;i<=n;i++)
    {
        scanf("%s",s+1);
        l=strlen(s+1);
        p=0;
        for (int j=1;j<=l;j++)
        {
            if (!trie[p][s[j]-'a'+1]) trie[p][s[j]-'a'+1]=++t1;
            p=trie[p][s[j]-'a'+1];
            v[p].push_back(i);
        }
    }
    node[0]=1;
    que[hd=tl=1]=0;
    while (hd<=tl)
    {
        u=que[hd++];
        for (int i=1;i<=26;i++)
            if (trie[u][i])
            {
                node[trie[u][i]]=add(node[u],i);
                que[++tl]=trie[u][i];
            }
    }
    dfs1(0,1);
    for (int i=1;i<=tot;i++) son[fail[i]].push_back(i);
    dfs2(1);
    for (int i=1;i<=clo;i++)
    {
        for (vector<int>::iterator it=id[ord[i]].begin();it!=id[ord[i]].end();++it)
        {
            if (pre[*it]) dec(pre[*it]);
            pre[*it]=i;
            inc(i);
        }
        for (vector<pair<int,int> >::iterator it=qry[i].begin();it!=qry[i].end();++it)
            num[(*it).first]=query(i)-query((*it).second-1);
    }
    for (int i=1;i<=tot;i++)
        if (num[i]>=m) res[i]=val[i]-val[fail[i]];
    for (int i=1;i<=tot;i++)
    {
        mx=max(mx,val[i]);
        cnt[val[i]]++;
    }
    for (int i=1;i<=mx;i++) cnt[i]+=cnt[i-1];
    for (int i=1;i<=tot;i++) que[cnt[val[i]]--]=i;
    for (int i=1;i<=tot;i++) res[que[i]]+=res[fail[que[i]]];
    dfs3(0,1);
    for (int i=1;i<=n;i++) printf("%lld ",ans[i]);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值