bzoj 3277
十分之恶心的后缀自动机
(其实是水题,但是我太弱了...)
首先,有一个预备知识: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的更多相关文章
- BZOJ 3277 串 & BZOJ 3473 字符串 (广义后缀自动机、时间复杂度分析、启发式合并、线段树合并、主席树)
标签那么长是因为做法太多了... 题目链接: (bzoj 3277) https://www.lydsy.com/JudgeOnline/problem.php?id=3277 (bzoj 3473) ...
- BZOJ 3277 串 (广义后缀自动机)
3277: 串 Time Limit: 10 Sec Memory Limit: 128 MB Submit: 309 Solved: 118 [Submit][Status][Discuss] De ...
- BZOJ 3277: 串/ BZOJ 3473: 字符串 ( 后缀数组 + RMQ + 二分 )
CF原题(http://codeforces.com/blog/entry/4849, 204E), CF的解法是O(Nlog^2N)的..记某个字符串以第i位开头的字符串对答案的贡献f(i), 那么 ...
- 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 ...
- bzoj 3277 & bzoj 3473,bzoj 2780 —— 广义后缀自动机
题目:https://www.lydsy.com/JudgeOnline/problem.php?id=3277 https://www.lydsy.com/JudgeOnline/problem.p ...
- bzoj 3277: 串
Description 字符串是oi界常考的问题.现在给定你n个字符串,询问每个字符串有多少子串(不包括空串)是所有n个字符串中 至少k个字符串的子串(注意包括本身). Solution 出现 \(k ...
- bzoj 3277 串 后缀树+子树不同数个数
题目大意 给定\(n\)个字符串和\(k\) 对于每个字符串,输出它有多少个子串至少是\(k\)个字符串的子串(包括自己) 分析 建出广义后缀自动机 至少是\(k\)个字符串的子串就是求子树内不同数个 ...
- bzoj 3277: 串 & bzoj 3473: 字符串【后缀自动机||后缀数组】
建一个广义后缀自动机(每加完一个串都返回root),在parent树上dpsum记录合法长度,打着时间戳往上跳,最后每个串在自动机上跑一变统计答案即可. 后缀数组理解起来可能方便一点,但是难写,就只说 ...
- BZOJ 3277/3473 广义后缀自动机
说实话没啥难的. 建一棵广义后缀自动机,暴力自底向上更新即可. 时间复杂度非常玄学,但据说是可以过的. 要注意每个串中相同的子串的贡献是都要加进去的,开始因为这个被坑了好久 QAQ Code: #in ...
随机推荐
- SparkStreaming+Kafa+HBase
1. 总结一些概念: 安装zookeeper3.4.6 cp zoo_sample.cfg zoo.cfgvim zoo.cfg tickTime=2000initLimit=10syncLimit= ...
- [转帖]Oracle 12cR2使用经验
大规模升级来临,谈谈Oracle 12cR2使用经验 随着2019年2月13日,Oracle 19c (Oracle 12.2.0.3) for Exadata 版本发布,Oracle 12cR2体系 ...
- Python项目部署-使用Nginx部署Django项目
一.nginx介绍及部署 二.nginx部署路飞学城代码 nginx配置安装 同样,nginx也有很多的安装方式: 1)源码安装(运维偏向:规范,便于配置管理) 2)yum,rpm安装(为了效率可以选 ...
- How to proof MD5
F(X, Y, Z) = (X&Y) | ((~X) & Z) G(X, Y, Z) = (X&Z) | (Y & (~Z)) H(X, Y, Z) = X^Y^Z I ...
- Django初印象之视图(view)
一.view的初印象 一个视图函数(类),简称视图.我们发起web请求时,返回的web响应.[大家约定成俗将视图放置在项目(project)或应用程序(app)目录中的名为views.py的文件中.] ...
- servlet篇 之 跳转问题
servlet中的跳转: 跳转本质上是对文件传输操作 进行 封装. 例如:客户端访问一个servlet,在servlet中需要把已经写好的页面hello.html返回给客户端浏览器,需要用io来实现这 ...
- C#、Java和JS实现SHA256+BASE64加密总结
C#.Java和JS实现SHA256+BASE64加密总结 --莫非(www.muphy.me) 原理 首先,通过编码格式(UTF-8.ASCII等,如果含有汉字等字符,编码格式不同加密结果也不同)获 ...
- Codeforces Round #513 总结
首次正式的$Codeforces$比赛啊,虽然滚粗了,然而终于有$rating$了…… #A Phone Numbers 签到题,然而我第一次写挂了(因为把11看成8了……) 只需要判断一下有多少个 ...
- win+R启动列表
屌丝才用windows,无奈~ """ Win+R 快速启动的命令: 系统应用程序: calc - 启动计算器 charmap - 启动字符映射表 chkdsk - Ch ...
- python中assert()函数的使用
关于assert()函数的使用,主要参考博客https://blog.csdn.net/qq_37119902/article/details/79637578 assert函数主要是用来声明某个函数 ...