十分之恶心的后缀自动机

(其实是水题,但是我太弱了...)

首先,有一个预备知识:bzoj 2780https://blog.csdn.net/lleozhang/article/details/89365183

现在我们假定你会了这道题

然后我们来讨论这个问题:

套路是一样的:仍然建起广义后缀自动机,然后搞出parent树

首先我们要想一个问题:如何确定某一个子串在这些串中出现的次数呢?

回顾一下这条性质:一个子串一定是一个前缀的后缀

所以我们在建起的后缀自动机上跑每一个串,在跑到每一个节点的时候暴力跳pre指针,如果能跳到某一个节点就证明跳到的节点所对应的子串是现在跑到的节点的子串,那么我们累计一下每个节点被跳到的次数,也就是他对应的子串在不同串中出现的次数了。

对于每一个出现次数>=k的节点,我们记它的val为它的len-它pre的len,然后在parent树上累计从根节点到该节点路径上的权值和,然后再跑一遍串,将经过的节点的权值和累加即为对应串的答案。

很显然你并没有看懂

所以我们做出解释:

首先,出现次数大于k的节点,这个节点相对他pre指针指向节点所多出的子串个数为len之差,那么这就是权值的初始值

紧接着,我们能够发现:在parent树上如果一个子节点是合法的,那么父节点一定是合法的,因为父节点是子节点的后缀,所以我们对每个子节点去累计它祖宗节点的总贡献即可

最后,我们在后缀自动机上跑串,累加跑到点的贡献即可

(当然,本题在统计子串出现次数的时候并没有使用离线树状数组,这是因为...会T!!!)

(upd:离线树状数组并不会T,只是因为...我把数组开大了,直接导致bzoj误将mle判成tle,所以在下面也贴上了树状数组的代码)

#include <cstdio>
#include <cmath>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#include <queue>
#include <stack>
#include <vector>
#define ll long long
using namespace std;
struct SAM
{
int tranc[27];
int endpos;
int len;
int num;
int pre;
}s[200005];
struct Edge
{
int next;
int to;
}edge[200005];
char ch[100005];
int head[200005];
int edt[200005];
int ret[200005];
int ilen[200005];
int last[200005];
int sch[100005];
int huge[200005];
int tot;
int dep;
int cnt=1;
int las,siz;
int n,k;
void init()
{
memset(head,-1,sizeof(head));
cnt=1;
}
void add(int l,int r)
{
edge[cnt].next=head[l];
edge[cnt].to=r;
head[l]=cnt++;
} void ins(int c,int typ)
{
int nwp=++siz;
s[nwp].endpos=typ;
s[nwp].len=s[las].len+1;
int lsp;
for(lsp=las;lsp&&!s[lsp].tranc[c];lsp=s[lsp].pre)s[lsp].tranc[c]=nwp;
if(!lsp)
{
s[nwp].pre=1;
}else
{
int lsq=s[lsp].tranc[c];
if(s[lsq].len==s[lsp].len+1)
{
s[nwp].pre=lsq;
}else
{
int nwq=++siz;
s[nwq]=s[lsq];
s[nwq].len=s[lsp].len+1;
s[nwq].endpos=0;
s[lsq].pre=s[nwp].pre=nwq;
while(s[lsp].tranc[c]==lsq)s[lsp].tranc[c]=nwq,lsp=s[lsp].pre;
}
}
las=nwp;
}
void buildtree()
{
init();
for(int i=2;i<=siz;i++)add(s[i].pre,i);
}
void redfs(int x)
{
ret[x]+=ret[s[x].pre];
for(int i=head[x];i!=-1;i=edge[i].next)
{
int to=edge[i].to;
redfs(to);
}
}
int main()
{
scanf("%d%d",&n,&k);
las=++siz;
for(int i=1;i<=n;i++)
{
scanf("%s",ch+1);
ilen[i]=strlen(ch+1);
for(int j=1;j<=ilen[i];j++)ins(ch[j]-'a'+1,i),sch[++tot]=ch[j]-'a'+1;
las=1;
}
buildtree();
int lass=0;
for(int i=1;i<=n;i++)
{
int las=1;
for(int j=1;j<=ilen[i];j++)
{
int temp=s[las].tranc[sch[j+lass]];
las=temp;
while(temp!=1&&last[temp]!=i)
{
huge[temp]++;
last[temp]=i;
temp=s[temp].pre;
}
}
lass+=ilen[i];
}
for(int i=1;i<=siz;i++)if(huge[i]>=k)ret[i]=s[i].len-s[s[i].pre].len;
redfs(1);
lass=0;
for(int i=1;i<=n;i++)
{
int las=1;
ll rans=0;
for(int j=1;j<=ilen[i];j++)
{
las=s[las].tranc[sch[j+lass]];
rans+=1ll*ret[las];
}
printf("%lld ",rans);
lass+=ilen[i];
}
printf("\n");
return 0;
}
#include <cstdio>
#include <cmath>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#include <queue>
#include <stack>
using namespace std;
struct SAM
{
int tranc[27];
int endpos;
int len;
int num;
int pre;
}s[200005];
struct Edge
{
int next;
int to;
}edge[200005];
char ch[400005];
int sum[400005];
int head[400005];
int inr[400005];
int our[400005];
int last[400005];
int f[400005];
int edt[400005];
int ret[400005];
int inrt[400005];
int ilen[400005];
int sch[400005];
int tot;
int dep;
int cnt=1;
int las,siz;
int n,k;
void init()
{
memset(head,-1,sizeof(head));
cnt=1;
}
int lowbit(int x)
{
return x&(-x);
}
void update(int x,int y)
{
while(x<=dep)
{
sum[x]+=y;
x+=lowbit(x);
}
}
int get_sum(int x)
{
int ans=0;
while(x)
{
ans+=sum[x];
x-=lowbit(x);
}
return ans;
}
void add(int l,int r)
{
edge[cnt].next=head[l];
edge[cnt].to=r;
head[l]=cnt++;
}
void ins(int c,int typ)
{
int nwp=++siz;
s[nwp].endpos=typ;
s[nwp].len=s[las].len+1;
int lsp;
for(lsp=las;lsp&&!s[lsp].tranc[c];lsp=s[lsp].pre)s[lsp].tranc[c]=nwp;
if(!lsp)
{
s[nwp].pre=1;
}else
{
int lsq=s[lsp].tranc[c];
if(s[lsq].len==s[lsp].len+1)
{
s[nwp].pre=lsq;
}else
{
int nwq=++siz;
s[nwq]=s[lsq];
s[nwq].len=s[lsp].len+1;
s[nwq].endpos=0;
s[lsq].pre=s[nwp].pre=nwq;
while(s[lsp].tranc[c]==lsq)s[lsp].tranc[c]=nwq,lsp=s[lsp].pre;
}
}
las=nwp;
}
void buildtree()
{
init();
for(int i=2;i<=siz;i++)add(s[i].pre,i);
}
void dfs(int x)
{
inr[x]=++dep;
f[dep]=x;
for(int i=head[x];i!=-1;i=edge[i].next)
{
int to=edge[i].to;
dfs(to);
}
our[x]=++dep;
edt[dep]=x;
}
/*void bfs()
{
queue <int> M;
M.push(1);
while(!M.empty())
{
int u=M.front();
M.pop();
for(int i=1;i<=26;i++)
{
int to=s[u].tranc[i];
if(to)
{
inrt[to]--;
ret[to]+=ret[u];
if(!inrt[to])M.push(to);
}
}
}
}*/
void redfs(int x)
{
ret[x]+=ret[s[x].pre];
for(int i=head[x];i!=-1;i=edge[i].next)
{
int to=edge[i].to;
redfs(to);
}
}
int main()
{
scanf("%d%d",&n,&k);
las=++siz;
for(int i=1;i<=n;i++)
{
scanf("%s",ch+1);
ilen[i]=strlen(ch+1);
for(int j=1;j<=ilen[i];j++)ins(ch[j]-'a'+1,i),sch[++tot]=ch[j]-'a'+1;
las=1;
}
buildtree();
dfs(1);
for(int i=1;i<=dep;i++)
{
update(i,1);
if(last[s[f[i]].endpos])update(last[s[f[i]].endpos],-1);
last[s[f[i]].endpos]=i;
if(edt[i]&&get_sum(i)-get_sum(inr[edt[i]]-1)>=k+1)ret[edt[i]]+=s[edt[i]].len-s[s[edt[i]].pre].len;
else ret[edt[i]]=0;
}
redfs(1);
int lass=0;
for(int i=1;i<=n;i++)
{
int las=1;
int rans=0;
for(int j=1;j<=ilen[i];j++)
{
rans+=ret[las];
las=s[las].tranc[sch[j+lass]];
}
rans+=ret[las];
lass+=ilen[i];
printf("%d ",rans);
}
printf("\n");
return 0;
}

bzoj 3277的更多相关文章

  1. BZOJ 3277 串 & BZOJ 3473 字符串 (广义后缀自动机、时间复杂度分析、启发式合并、线段树合并、主席树)

    标签那么长是因为做法太多了... 题目链接: (bzoj 3277) https://www.lydsy.com/JudgeOnline/problem.php?id=3277 (bzoj 3473) ...

  2. BZOJ 3277 串 (广义后缀自动机)

    3277: 串 Time Limit: 10 Sec Memory Limit: 128 MB Submit: 309 Solved: 118 [Submit][Status][Discuss] De ...

  3. BZOJ 3277: 串/ BZOJ 3473: 字符串 ( 后缀数组 + RMQ + 二分 )

    CF原题(http://codeforces.com/blog/entry/4849, 204E), CF的解法是O(Nlog^2N)的..记某个字符串以第i位开头的字符串对答案的贡献f(i), 那么 ...

  4. bzoj 3277 串 && bzoj 3473 字符串 && bzoj 2780 [Spoj]8093 Sevenk Love Oimaster——广义后缀自动机

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=3277 https://www.lydsy.com/JudgeOnline/problem.p ...

  5. bzoj 3277 & bzoj 3473,bzoj 2780 —— 广义后缀自动机

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=3277 https://www.lydsy.com/JudgeOnline/problem.p ...

  6. bzoj 3277: 串

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

  7. bzoj 3277 串 后缀树+子树不同数个数

    题目大意 给定\(n\)个字符串和\(k\) 对于每个字符串,输出它有多少个子串至少是\(k\)个字符串的子串(包括自己) 分析 建出广义后缀自动机 至少是\(k\)个字符串的子串就是求子树内不同数个 ...

  8. bzoj 3277: 串 & bzoj 3473: 字符串【后缀自动机||后缀数组】

    建一个广义后缀自动机(每加完一个串都返回root),在parent树上dpsum记录合法长度,打着时间戳往上跳,最后每个串在自动机上跑一变统计答案即可. 后缀数组理解起来可能方便一点,但是难写,就只说 ...

  9. BZOJ 3277/3473 广义后缀自动机

    说实话没啥难的. 建一棵广义后缀自动机,暴力自底向上更新即可. 时间复杂度非常玄学,但据说是可以过的. 要注意每个串中相同的子串的贡献是都要加进去的,开始因为这个被坑了好久 QAQ Code: #in ...

随机推荐

  1. SpringCloud入门(一)

    一.微服务概述 1.什么是微服务 目前的微服务并没有一个统一的标准,一般是以业务来划分将传统的一站式应用,拆分成一个个的服务,彻底去耦合,一个微服务就是单功能业务,只做一件事. 与微服务相对的叫巨石 ...

  2. python之路2-字符串操作

    1.我们可以用单引号(‘)或双引号(” ")创建字符串,并为字符串分配一个变量,例如: name = "张三" name = "wangsan" 字符 ...

  3. RCTF 2017 easyre-153

    die查一下发现是upx壳 直接脱掉 ELF文件 跑一下: 没看懂是什么意思 随便输一个数就结束了 ida打开 看一下: pipe是完成两个进程之间通信的函数 1是写,0是读 fork是通过系统调用创 ...

  4. IDEA的 mybatis插件报错 - IDE Fatal Errors

    IDE Fatal Errors Exception in plugin Mybatis plugin. A minute ago. Occurred once since the last clea ...

  5. opencv 增强现实(一):特征点检测

    import cv2 as cv import numpy as np def draw_keypoints(img, keypoints): for kp in keypoints: x, y = ...

  6. 最大公约数和最小公倍数(Greatest Common Divisor and Least Common Multiple)

    定义: 最大公约数(英语:greatest common divisor,gcd).是数学词汇,指能够整除多个整数的最大正整数.而多个整数不能都为零.例如8和12的最大公因数为4. 最小公倍数是数论中 ...

  7. 剑指Offer_编程题_20

    题目描述 从上往下打印出二叉树的每个节点,同层节点从左至右打印.   /* struct TreeNode { int val; struct TreeNode *left; struct TreeN ...

  8. C++回顾day03---<类型转换>

    一:C++类型转换 (一)static_cast<>() 静态类型转换:基本类型可以转换但是指针类型不允许.可以进行隐式类型转换 double n=1.23 int m=static_ca ...

  9. 第十六节: EF的CodeFirst模式通过Fluent API修改默认协定

    一. 简介 1. 优先级:Fluent API > data annotations > default conventions. 2. 所有的Fluent API配置都要在 OnMode ...

  10. 页面加载过渡页 loading plugin css

    是前文 plugin 示例 loading 中需要用到的样式.现在样式扩充为 5类.loadtwo 使用到了bgimg /*loading animation loading one*/ /* loa ...