首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >BZOJ3277: 串(广义后缀自动机)

BZOJ3277: 串(广义后缀自动机)

作者头像
attack
发布2018-07-27 15:05:27
4370
发布2018-07-27 15:05:27
举报

Time Limit: 10 Sec  Memory Limit: 128 MB

Submit: 1196  Solved: 478

[Submit][Status][Discuss]

Description

字符串是oi界常考的问题。现在给定你n个字符串,询问每个字符串有多少子串(不包括空串)是所有n个字符串中

至少k个字符串的子串(注意包括本身)。

Input

第一行两个整数n,k。

接下来n行每行一个字符串。

n,k,l<=100000

Output

输出一行n个整数,第i个整数表示第i个字符串的答案。

Sample Input

3 1 abc a ab

Sample Output

6 1 3

HINT

Source

广义后缀自动机?就是把一坨字符串建到一个后缀自动机上??

不过好难理解啊qwq。。

对于这题,首先我们要知道几个定理

1.节点$i$表示的本质不同的字符串可以由$len[i] - len[fa[i]]$得到

2.一个串的子串 等价于 一个串所有前缀的所有后缀

这样子串就转换为求一个串的前缀的所有后缀的问题

前缀可以枚举,问题转换为求一个字符串的各个后缀在其他字符串中出现了多少次

这样我们可以把广义后缀自动机建出来,然后暴力沿着$parent$边跑,这样可以枚举出所有后缀

同时为了不重复枚举,我们需要记录下每个后缀是否已经被枚举过了

这样我们就可以知道一个状态出现的次数是否$>= K$,接下来我们只要统计出这个状态出现的次数就行了

根据定理$1$,这个很好统计

然后就做完啦

#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
const int MAXN = 1e6 + 10;
string s[MAXN];
int N, K;
int fa[MAXN], len[MAXN], ch[MAXN][26], root = 1, last = 1, tot = 1, times[MAXN];
void insert(int x) {
    int now = ++tot, pre = last; last = now; len[now] = len[pre] + 1;
    for(; pre && !ch[pre][x]; pre = fa[pre]) ch[pre][x] = now;
    if(!pre) fa[now] = root;
    else {
        int q = ch[pre][x];
        if(len[q] == len[pre] + 1) fa[now] = q;
        else {
            int nows = ++tot; len[nows] = len[pre] + 1;
            memcpy(ch[nows], ch[q], sizeof(ch[q]));
            fa[nows] = fa[q]; fa[q] = fa[now] = nows;
            for(; pre && ch[pre][x] == q; pre = fa[pre]) ch[pre][x] = nows;
        } 
    }
}
int vis[MAXN], sum[MAXN];
void GetTimes() {//求出每一个状态在几个字符串出现过 
    for(int i = 1; i <= N; i++) {
        int now = root;
        for(int j = 0; j < s[i].length(); j++) {
            now = ch[now][s[i][j] - 'a'];//枚举每一个前缀 
            int t = now;
            while(t && vis[t] != i) vis[t] = i, times[t]++, t = fa[t];//枚举每一个后缀 
        }
    }
}
void dfs(int x) {
    if(x == root || vis[x]) return ;
    vis[x] = 1; 
    dfs(fa[x]); 
    sum[x] += sum[fa[x]];
}
int main() {
#ifdef WIN32
    freopen("a.in", "r", stdin);
#endif
    ios::sync_with_stdio(0);
    cin >> N >> K;
    for(int i = 1; i <= N; i++) cin >> s[i];
    for(int i = 1; i <= N; i++) {
        last = 1;
        for(int j = 0; j < s[i].length(); j++)
            insert(s[i][j] - 'a');
    }
    
    GetTimes();
    
    for(int i = 1; i <= tot; i++) sum[i] = (times[i] >= K) * (len[i] - len[fa[i]]);//i状态所表示的子串集合对答案的贡献
    memset(vis, 0, sizeof(vis));
    for(int i = 1; i <= tot; i++) dfs(i);
    for(int i = 1; i <= N; i++) {
        int ans = 0, now = root;
        for(int j = 0; j < s[i].length(); j++)
            now = ch[now][s[i][j] - 'a'], ans += sum[now];
        //枚举前缀,算出每一个前缀所包含的后缀对答案啊的贡献 
        printf("%d ", ans);
    }
    
    return 0;
}
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2018-06-28 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Description
  • Input
  • Output
  • Sample Input
  • Sample Output
  • HINT
  • Source
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档