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 + ;
string s[MAXN];
int N, K;
int fa[MAXN], len[MAXN], ch[MAXN][], root = , last = , tot = , times[MAXN];
void insert(int x) {
int now = ++tot, pre = last; last = now; len[now] = len[pre] + ;
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] + ) fa[now] = q;
else {
int nows = ++tot; len[nows] = len[pre] + ;
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 = ; i <= N; i++) {
int now = root;
for(int j = ; 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] = ;
dfs(fa[x]);
sum[x] += sum[fa[x]];
}
int main() {
#ifdef WIN32
freopen("a.in", "r", stdin);
#endif
ios::sync_with_stdio();
cin >> N >> K;
for(int i = ; i <= N; i++) cin >> s[i];
for(int i = ; i <= N; i++) {
last = ;
for(int j = ; j < s[i].length(); j++)
insert(s[i][j] - 'a');
} GetTimes(); for(int i = ; i <= tot; i++) sum[i] = (times[i] >= K) * (len[i] - len[fa[i]]);//i状态所表示的子串集合对答案的贡献
memset(vis, , sizeof(vis));
for(int i = ; i <= tot; i++) dfs(i);
for(int i = ; i <= N; i++) {
int ans = , now = root;
for(int j = ; j < s[i].length(); j++)
now = ch[now][s[i][j] - 'a'], ans += sum[now];
//枚举前缀,算出每一个前缀所包含的后缀对答案啊的贡献
printf("%d ", ans);
} return ;
}

 

最新文章

  1. 【工具使用】mac电脑使用技巧
  2. BZOJ 4196: [Noi2015]软件包管理器 [树链剖分 DFS序]
  3. volatile
  4. Linux远程复制命令SCP
  5. 转载:手机网页制作的认识(有关meta标签)
  6. Linux:文件权限
  7. php中ckeditor(Fckeditor)的配置方法
  8. 算法系列7《CVN》
  9. Chapter 7 Windows下pycaffe的使用之draw_net.py
  10. HDU 5927 Auxiliary Set 【DFS+树】(2016CCPC东北地区大学生程序设计竞赛)
  11. OBIEE 简介
  12. test错误记录
  13. Volatile和Synchronized对可见性和原子性的支持
  14. MNIST-NameError: name ‘input_data’ is not defined解决办法
  15. 想在Java中实现Excel和Csv的导出吗?看这就对了
  16. kubernetes系列06—kubernetes资源清单定义入门
  17. iOS:基于RTMP的视频推流
  18. windows下apache httpd2.4.26集群完整搭建例子:下载、启动、tomcat集群例子
  19. SQL SERVER占用CPU过高优化
  20. 基于pandas python的美团某商家的评论销售数据分析(可视化)

热门文章

  1. JavaScirpt(JS)——js介绍及ECMAScript
  2. jQuery三——筛选方法、事件
  3. php有经纬度计算距离
  4. String字符串操作
  5. 学习JVM虚拟机原理总结
  6. monkeyrunner多点触摸
  7. 爬虫入门之jsonPath PhantomJS与 selenium详解(六)
  8. 【CentOS】在Centos7 下无图形界面安装 Oracle11g
  9. vue+node+mongoose踩过的坑
  10. One Order行项目里Item Category是怎么计算出来的