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 ...
随机推荐
- Java 如何存取MySQL datetime类型
1 在java中只有Date类型,这样数据存储到MySQL会出现问题,前台提交的数据,比如2018-03-20 17:30:59,后台用Date接受的时候,由于Date只精确到天,所以默认接收时间为2 ...
- [硬件]超能课堂(181):我们为什么需要4+8pin CPU供电接口?
超能课堂(181):我们为什么需要4+8pin CPU供电接口? https://www.expreview.com/68008.html 之前算过TDP 来计算机器的功耗 发现自己 理解的还是有偏差 ...
- CodeForces 280B Maximum Xor Se
题目链接:http://codeforces.com/contest/280/problem/B 题目大意: 给定一个由n个数组成的一个序列,s[l..r] (1 ≤ l < r ≤ n)代表原 ...
- dubbo 使用zookeeper 出现 Dubbo客户端调用报错NullPointerException
现在将网上的方法总结一下 方法一:.https://blog.csdn.net/u011294519/article/details/81810631 dubbo-provider.xml:提供者先扫 ...
- LeetCode-876 链表的中间结点
对于链表的中某个位置结点的定位一般都会用到两个链表结点指针,例如链表倒数第K个结点问题使用的是先后指针,该题中用到的快慢指针. 本题的具体解法就是快指针走两步.慢指针走一步知道遍历完结点,重点是分清题 ...
- spring cloud实战与思考(二) 微服务之间通过fiegn上传一组文件(上)
需求场景: 微服务之间调用接口一次性上传多个文件. 上传文件的同时附带其他参数. 多个文件能有效的区分开,以便进行不同处理. Spring cloud的微服务之间接口调用使用Feign.原装的Feig ...
- 复习IIC协议---以AT24C02为例
1.总纲--复习IIC(inter integrated circuit)协议以及自己顺便读一下数据手册. /********************************************* ...
- L2-006 树的遍历 (25 分) (根据后序遍历与中序遍历建二叉树)
题目链接:https://pintia.cn/problem-sets/994805046380707840/problems/994805069361299456 L2-006 树的遍历 (25 分 ...
- (一)Qt5模块,QtCreator常用快捷键,命名规范
常用快捷键 1)帮助文件:F1 (光标在函数名字或类名上,按 F1 即可跳转到对应帮助文档,查看其详细用法) 2).h 文件和对应.cpp 文件切换:F4 3)编译并运行:Ctrl + R 4)函数声 ...
- django-url的分发
1)url的分发: 1,首先在全局的url里面的路径中写好,你要分发的路径名. 2,并且在你要分发的路径下,创好新的url文件. 在分发的路径名里面,把全局url里面的代码,复制过来 3,最后在浏览器 ...