bzoj 3473: 字符串 (后缀自动机)

题目描述

传送门

题目大意:给定n个字符串,询问每个字符串有多少子串(不包括空串)是所有n个字符串中至少k个字符串的子串?

题解

同bzoj3277

代码

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#define N 200003
#define LL long long
using namespace std;
int tot,n,k,point[N],nxt[N],v1[N],ch[N][30],fa[N],c[N];
int v[N],pos[N],l[N],mark[N],cnt,root,last,np,q,p,nq,head[N],next[N];
char s[N];
LL ct[N],ans[N];
void add(int x,int y)
{
    tot++; nxt[tot]=point[x]; point[x]=tot; v1[tot]=y;
}
void extend(int x,int col)
{
    int c=s[x]-'a'+1; np=++cnt;
    p=last; last=np; l[np]=l[p]+1;
    for (;!ch[p][c]&&p;p=fa[p]) ch[p][c]=np;
    if (!p) fa[np]=root;
    else {
        q=ch[p][c];
        if (l[p]+1==l[q]) fa[np]=q;
        else {
            nq=++cnt; l[nq]=l[p]+1;
            memcpy(ch[nq],ch[q],sizeof(ch[nq]));
            fa[nq]=fa[q]; mark[nq]=mark[q]; ct[nq]=ct[q];
            fa[q]=fa[np]=nq;
            for (;ch[p][c]==q;p=fa[p]) ch[p][c]=nq;
        }
    }
    add(col,np);
    for (;np;np=fa[np])
     if (mark[np]!=col) {
        mark[np]=col; 
        ct[np]++;
     }
     else break;
}
void build(int x,int y)
{
    tot++; next[tot]=head[x]; head[x]=tot; v[tot]=y;
}
void dfs(int x)
{
    for (int i=head[x];i;i=next[i]){
        ans[v[i]]+=ans[x];
        dfs(v[i]);
    }
}
int main()
{
    freopen("a.in","r",stdin);
    scanf("%d%d",&n,&k);
    root=++cnt;
    for (int i=1;i<=n;i++) {
        scanf("%s",s+1); int len=strlen(s+1);
        last=root;
        for (int j=1;j<=len;j++) extend(j,i);
    }
    for (int i=1;i<=cnt;i++) v[l[i]]++;
    for (int i=1;i<=cnt;i++) v[i]+=v[i-1];
    for (int i=1;i<=cnt;i++) pos[v[l[i]]--]=i;
    tot=0;
    for (int i=1;i<=cnt;i++) {
        int t=pos[i];
        build(fa[t],t);
        ans[t]=(ct[t]>=k?l[t]-l[fa[t]]:0);
    }
    c[1]=0;
    dfs(1);
    for (int i=1;i<=n;i++) {
        LL sum=0;
        for (int j=point[i];j;j=nxt[j])
         sum+=ans[v1[j]];
        printf("%lld ",sum);
    }
    printf("\n");
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值