BZOJ3473&&BZOJ3277串

题面

自己找去

HINT

对于所有串建立一个广义后缀自动机,对于每一个节点开一个set表示这个节点接受的子串在哪些串里出现过,然后在parent tree上做启发式合并,理论复杂度应该是\(nlog_{n}^2\)。

广义后缀自动机

这个广义后缀自动机在建立的时候和后缀自动机时是基本差不多的,就是在每加入一个新串的时候,把\(last=root\)就好了

如何找匹配大等于k次

首先因为每个节点的fa表示的子串都是现在这个节点的后缀,所以fa出现的次数一定\(>=\)当前节点出现的次数(这个东西可以感性认知进行证明),另外,如果一个子串出现次数小等于k,在这个子串后面加一个字符后它的出现次数也不可能大于k次

一句话题解

建立一个广义后缀自动机,然后在每个节点上开个set记录在几个串上出现,然后parent tree上启发式合并set,查询答案的时候不断往父亲节点跳,直到满足条件,然后\(\sum{node[u].len}\)

#include<bits/stdc++.h>
#include<set>
using namespace std;
const int maxn=200010;
inline int read(){
int w=0,f=1;
char ch=getchar();
while(ch<'0'||ch>'9'){
if(ch=='-') f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9'){
w=(w<<3)+(w<<1)+ch-48;
ch=getchar();
}
return w*f;
}
int n,m;
bool debug;
set<int> s[maxn];
set<int>::iterator it;
struct SUFFIXAUTOMATON{
struct Node{
int len,fa;
map<int,int> ch;
}node[2000010];
int lst,root,tot;
inline void init(){
lst=root=tot=1;return;
}
inline void extend(int now,int id){
int p=lst;tot++;lst=tot;int np=tot;
node[np].len=node[p].len+1;s[np].insert(id);
while(p&&!node[p].ch[now]){
node[p].ch[now]=np;
p=node[p].fa;
}
if(!p) node[np].fa=1;
else{
int q=node[p].ch[now];
if(node[q].len==node[p].len+1){
node[np].fa=q;
}
else{
int nq=++tot;node[nq]=node[q];
node[nq].len=node[p].len+1;
node[q].fa=nq;node[np].fa=nq;
while(p&&node[p].ch[now]==q){
node[p].ch[now]=nq;
p=node[p].fa;
}
}
}
}
}SAM;
string ch[maxn];
int sum[maxn];
struct Edge{
int from,to,next;
}edge[maxn*6];
int cnt,head[maxn];
inline void addedge(int u,int v){
cnt++;
edge[cnt].from=u;
edge[cnt].to=v;
edge[cnt].next=head[u];
head[u]=cnt;
}
inline void dfs(int u){
for(int i=head[u];i;i=edge[i].next){
int v=edge[i].to;dfs(v);
if(s[u].size()<s[v].size()) swap(s[u],s[v]);
for(it=s[v].begin();it!=s[v].end();it++){
s[u].insert(*it);
}
}
sum[u]=s[u].size();
return;
}
int main(){
n=read();m=read();SAM.init();
for(int i=1;i<=n;i++){
cin>>ch[i];int len=ch[i].length();
for(int j=0;j<len;j++){
SAM.extend(ch[i][j]-'a'+1,i);
}
SAM.lst=1;
}
for(int i=1;i<=SAM.tot;i++){
if(SAM.node[i].fa) addedge(SAM.node[i].fa,i);
}
dfs(SAM.root);
if(m>n){
for(int i=1;i<=n;i++){
printf("0 ");
}
return 0;
}
for(int i=1;i<=n;i++){
long long ans=0;int u=SAM.root;int len=ch[i].length();
for(int j=0;j<len;j++){
u=SAM.node[u].ch[ch[i][j]-'a'+1];
while(sum[u]<m) u=SAM.node[u].fa;
ans+=SAM.node[u].len;
}
printf("%lld ",ans);
}
return 0;
}

你以为这就完了吗,naive

我在写最后那个统计答案的时候,想的做法长这样

for(int i=1;i<=n;i++){
long long ans=0;int u=SAM.root;int len=ch[i].length();
for(int j=0;j<len;j++){
u=SAM.node[u].ch[ch[i][j]-'a'+1];int p=u;
while(sum[p]<m) p=SAM.node[p].fa;
ans+=SAM.node[p].len;
}
printf("%lld ",ans);
}

我是想每次匹配完向上跳,但是在bzoj上它TLE了,然后我想了一个妙妙做法,并查集优化

inline int find(int x){
if(sum[x]>=m) return x;
else return f[x]=find(f[x]);
}

然后跑得飞快,就过了

BZOJ3473&&BZOJ3277串的更多相关文章

  1. [BZOJ3473][BZOJ3277]字符串

    [BZOJ3473][BZOJ3277]字符串 试题描述 给定 \(n\) 个字符串,询问每个字符串有多少子串(不包括空串)是所有 \(n\) 个字符串中至少 \(k\) 个字符串的子串? 输入 第一 ...

  2. bzoj3473: 字符串 && bzoj3277串

    3473: 字符串 Time Limit: 20 Sec  Memory Limit: 256 MBSubmit: 121  Solved: 53[Submit][Status][Discuss] D ...

  3. bzoj3473字符串&bzoj3277串

    题意:给定n个字符串,询问每个字符串有多少子串(不包括空串)是所有n个字符串中至少k个字符串的子串.注意本质相同的子串多次出现算多次,如1 1 aaa这组数据答案为6,贡献1WA.代码里有些部分是为了 ...

  4. 【bzoj3277/bzoj3473】串/字符串 广义后缀自动机

    题目描述 字符串是oi界常考的问题.现在给定你n个字符串,询问每个字符串有多少子串(不包括空串)是所有n个字符串中至少k个字符串的子串(注意包括本身). 输入 第一行两个整数n,k.接下来n行每行一个 ...

  5. BZOJ3277 串 和 BZOJ3473 字符串

    字符串 给定n个字符串,询问每个字符串有多少子串(不包括空串)是所有n个字符串中至少k个字符串的子串? 分析 参照自为风月马前卒和Candy?的题解. 广义后缀自动机不就是把很多串的SAM建到了一个S ...

  6. BZOJ3277 串(后缀数组+二分答案+主席树)

    因为不会SAM,考虑SA.将所有串连起来并加分隔符,每次考虑计算以某个位置开始的子串有多少个合法. 对此首先二分答案,找到名次数组上的一个区间,那么只需要统计有多少个所给串在该区间内出现就可以了.这是 ...

  7. BZOJ3277——串

    0.题意:给定你n个字符串,询问每个字符串有多少子串(不包括空串)是所有n个字符串中至少k个字符串的子串(注意包括本身). 1.分析:这个题我问了吴大爷做法 首先建立后缀自动机,然后利用离线搞出每一个 ...

  8. bzoj3277 串 (后缀数组+二分答案+ST表)

    常见操作:先把所有串都连到一起,但中间加上一个特殊的符号(不能在原串中/出现过)作为分割 由于全部的子串就等于所有后缀的所有前缀,那我们对于每一个后缀,去求一个最长的前缀,来满足这个前缀在至少K个原串 ...

  9. 【文文殿下】[BZOJ3277] 串

    Description 字符串是oi界常考的问题.现在给定你n个字符串,询问每个字符串有多少子串(不包括空串)是所有n个字符串中 至少k个字符串的子串(注意包括本身) Input 第一行两个整数n,k ...

随机推荐

  1. chromedriver和firefox driver的安装过程

    环境:ubuntu14.04, python2.7 selenium2.0 文章参考出处:http://blog.csdn.net/heybob/article/details/52922645 ch ...

  2. 1213 - Fantasy of a Summation

    1213 - Fantasy of a Summation         If you think codes, eat codes then sometimes you may get stres ...

  3. centos7安装node.js

    安装版本:node-v10.15.3 一.安装必要的编译软件包 # yum install gcc gcc-c++ -y 二.从源码下载Nodejs 进入官网选择自己需要的版本 https://nod ...

  4. Python实现进度条的4种方式

    这里只列举了部分方法,其他方法或python库暂时还没使用到 马蜂窝刷粉丝[微信:156150954] 1.不用库,直接打印: 代码样例: import time #demo1 def process ...

  5. 网易MuMu模拟器不显示Menu(菜单)键的解决办法

    解决方法一: 前提:需要一个键盘 步骤: 1.直接按下键盘上的Menu键. 解决方法二: 前提:需要Root之后的文件浏览器 步骤: 1.在文件管理器中打开 /System 文件夹: 2.复制 bui ...

  6. mysql必知必会--创建计算字段

    计算字段 存储在数据库表中的数据一般不是应用程序所需要的格式.下面举 几个例子. * 如果想在一个字段中既显示公司名,又显示公司的地址,但这两 个信息一般包含在不同的表列中. * 城市.州和邮政编码存 ...

  7. HTML连载67-手风琴效果、2D转换模块

    一.手风琴效果 <style> *{ margin:0; padding:0; } ul{ width: 960px; height: 300px; margin:100px auto; ...

  8. 【daily】Java枚举 - fastjson对enum的处理

    目的 1.枚举值转换成完全的json: 2.对象中的枚举成员完全转换成json: 3.枚举类的全部值转换成json: 枚举定义 public enum SongsEnum { SAFE_AND_SOU ...

  9. 剑指offer-面试题40-最小的k个数-最大堆

    /* 题目: 输入n个整数,找出其中最小的K个数.例如输入4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3,4,. */ /* 思路: 利用最大堆,C++中使用multiset& ...

  10. Fhq Treap [FhqTreap 学习笔记]

    众所周知 Fhq Treap 是 fhq 神仙研究出来的平衡树- 具体实现 每个点实现一个 \(\text{rnd}\) 表示 rand 的值 为什么要 rand 呢 是为了保证树高为 \(\log ...