51nod1600 Simple KMP
题目描述
对于一个字符串|S|,我们定义fail[i],表示最大的x使得S[1..x]=S[i-x+1..i],满足(x<i)
显然对于一个字符串,如果我们将每个0<=i<=|S|看成一个结点,除了i=0以外i向fail[i]连边,这是一颗树的形状,根是0
我们定义这棵树是G(S),设f(S)是G(S)中除了0号点以外所有点的深度之和,其中0号点的深度为-1
定义key(S)等于S的所有非空子串S'的f(S')之和
给定一个字符串S,现在你要实现以下几种操作:
1.在S最后面加一个字符
2.询问key(S)
题解
遇到这种对所有子串统计的问题,考虑差分答案数组。
我们将答案数组二次差分之后,只需要算每个字符在所有以它结尾的子串中的贡献即可。
考虑这个\(border\)树的深度有什么意义。
观察或手玩即可发现,对于某个串来说,末尾字符位置的深度就是这个串前缀等于后缀的串的数,就是每一对前缀等于后缀都会对这个位置有1的贡献。
所以对于i位置,考虑前\(i-1\)个位置的字符串,我们现在只需要求所有以这个点结尾的串在前\(i-1\)的字符串的出现次数的和。
用链剖或\(LCT\)维护即可。
代码
#include<bits/stdc++.h>
#define N 200002
using namespace std;
typedef long long ll;
const int mod=1e9+7;
int la[N<<2],num[N],tot,head[N],fa[N],deep[N],son[N],dfn[N],_tag[N],top[N],n;
char s[N];
ll tr[N<<2],size[N<<2],ans[N];
inline ll rd(){
ll x=0;char c=getchar();bool f=0;
while(!isdigit(c)){if(c=='-')f=1;c=getchar();}
while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
return f?-x:x;
}
struct edge{int n,to;}e[N];
inline void MOD(ll &x){x=x>=mod?x-mod:x;}
struct SAM_t{
int ch[N][26],fa[N],l[N],cnt,last;
SAM_t(){cnt=last=1;}
inline void ins(int x,int id){
int p=last,np=++cnt;l[np]=l[p]+1;last=np;num[id]=cnt;
for(;p&&!ch[p][x];p=fa[p])ch[p][x]=np;
if(!p)fa[np]=1;
else{
int q=ch[p][x];
if(l[p]+1==l[q])fa[np]=q;
else{
int nq=++cnt;l[nq]=l[p]+1;
memcpy(ch[nq],ch[q],sizeof(ch[nq]));
fa[nq]=fa[q];fa[q]=fa[np]=nq;
for(;ch[p][x]==q;p=fa[p])ch[p][x]=nq;
}
}
}
}sam;
inline void pushdown(int cnt){
la[cnt<<1]+=la[cnt];
la[cnt<<1|1]+=la[cnt];
MOD(tr[cnt<<1]+=size[cnt<<1]*la[cnt]%mod);
MOD(tr[cnt<<1|1]+=size[cnt<<1|1]*la[cnt]%mod);
la[cnt]=0;
}
ll upd(int cnt,int l,int r,int L,int R){;
if(l>=L&&r<=R){
ll x=tr[cnt];
MOD(tr[cnt]+=size[cnt]);
la[cnt]++;
return x;
}
int mid=(l+r)>>1;
if(la[cnt])pushdown(cnt);ll ans=0;
if(mid>=L)MOD(ans+=upd(cnt<<1,l,mid,L,R));
if(mid<R)MOD(ans+=upd(cnt<<1|1,mid+1,r,L,R));
MOD(tr[cnt]=tr[cnt<<1]+tr[cnt<<1|1]);
return ans;
}
void build(int cnt,int l,int r){
if(l==r){
size[cnt]=sam.l[_tag[l]]-sam.l[sam.fa[_tag[l]]];
return;
}
int mid=(l+r)>>1;
build(cnt<<1,l,mid);build(cnt<<1|1,mid+1,r);
MOD(size[cnt]=size[cnt<<1]+size[cnt<<1|1]);
}
inline void add(int u,int v){e[++tot].n=head[u];e[tot].to=v;head[u]=tot;}
void dfs1(int u){
size[u]=1;
for(int i=head[u];i;i=e[i].n)if(e[i].to!=fa[u]){
int v=e[i].to;fa[v]=u;deep[v]=deep[u]+1;
dfs1(v);
size[u]+=size[v];
if(size[v]>size[son[u]])son[u]=v;
}
}
void dfs2(int u){
dfn[u]=++dfn[0];_tag[dfn[0]]=u;
if(!top[u])top[u]=u;
if(son[u])top[son[u]]=top[u],dfs2(son[u]);
for(int i=head[u];i;i=e[i].n)if(e[i].to!=fa[u]&&e[i].to!=son[u])dfs2(e[i].to);
}
ll work(int x){
ll ans=0;
while(top[x]!=top[1]){
MOD(ans+=upd(1,1,sam.cnt,dfn[top[x]],dfn[x]));
x=fa[top[x]];
}
MOD(ans+=upd(1,1,sam.cnt,dfn[1],dfn[x]));
return ans;
}
int main(){
n=rd();
scanf("%s",s+1);
for(int i=1;i<=n;++i)sam.ins(s[i]-'a',i);
for(int i=1;i<=sam.cnt;++i)if(sam.fa[i])add(sam.fa[i],i);
dfs1(1);dfs2(1);
build(1,1,sam.cnt);
for(int i=1;i<=n;++i){
int x=work(num[i]);
MOD(ans[i]=ans[i-1]+x);
}
for(int i=1;i<=n;++i){
MOD(ans[i]+=ans[i-1]);
printf("%lld\n",ans[i]);
}
return 0;
}
51nod1600 Simple KMP的更多相关文章
- 51Nod 1600 Simple KMP SAM+LCT/树链剖分
1600 Simple KMP 对于一个字符串|S|,我们定义fail[i],表示最大的x使得S[1..x]=S[i-x+1..i],满足(x<i)显然对于一个字符串,如果我们将每个0<= ...
- 51Nod 1600 Simple KMP 解题报告
51Nod 1600 Simple KMP 对于一个字符串\(|S|\),我们定义\(fail[i]\),表示最大的\(x\)使得\(S[1..x]=S[i-x+1..i]\),满足\((x<i ...
- 【51nod1006】simple KMP
原题意看的挺迷糊的,后来看了http://blog.csdn.net/YxuanwKeith/article/details/52351335大爷的题意感觉清楚的多…… 做法也非常显然了,用树剖维护后 ...
- 51nod 1600 Simple KMP【后缀自动机+LCT】【思维好题】*
Description 对于一个字符串|S|,我们定义fail[i],表示最大的x使得S[1..x]=S[i-x+1..i],满足(x<i) 显然对于一个字符串,如果我们将每个0<=i&l ...
- 51nod 1600 Simple KMP
又被机房神犇肉丝哥哥和glory踩爆了 首先这个答案的输出方式有点套路,当前的答案=上一个答案+每一个后缀的f值=上一个答案+上一次算的每个后缀的f值+当前每个后缀的深度 这个题意给了个根深度为-1有 ...
- CSU 2056 a simple game (正反进行KMP)超级好题!!!
Description 这一天,小A和小B在玩一个游戏,他俩每人都有一个整数,然后两人轮流对他们的整数进行操作,每次在下列两个操作任选一个: (1)对整数进行翻转,如1234翻转成4321 ,1200 ...
- ACM: SCU 4438 Censor - KMP
SCU 4438 Censor Time Limit:0MS Memory Limit:0KB 64bit IO Format:%lld & %llu Practice D ...
- 从头到尾彻底理解KMP
从头到尾彻底理解KMP 作者:July 时间:最初写于2011年12月,2014年7月21日晚10点 全部删除重写成此文,随后的半个多月不断反复改进. 1. 引言 本KMP原文最初写于2年多前的201 ...
- KMP详解
原文: http://blog.csdn.net/v_july_v/article/details/7041827 从头到尾彻底理解KMP 1. 引言 本KMP原文最初写于2年多前的2011年12月, ...
随机推荐
- vue-methods方法与computed计算属性的差别
好吧,我就是单纯的举个例子:实现显示变量 message 的翻转字符串 第一种:methods:我们可以通过在表达式中调用方法来达到同样的效果: 第二种:computed:计算属性 上面的2中方法都实 ...
- idea下载和设置自动翻译(有道)
1:下载 点击file,点击settings,找到plugins,之后所搜translation并下载,他会自动从新启动idea 2:设置translation 3:这个应用ID和秘钥需要在有道智云去 ...
- [Python3 填坑] 008 索引君的朋友 in
目录 1. print( 坑的信息 ) 2. 开始填坑 (1) 前情提要 (2) 索引君的朋友 in 上线 (3) 既然说了 in,不妨再说一说 not in (4) 一些补充 1. print( 坑 ...
- Git利用命令行提交代码步骤
利用命令行提交代码步骤进入你的项目目录1:拉取服务器代码,避免覆盖他人代码git pull2:查看当前项目中有哪些文件被修改过git status具体状态如下:1:Untracked: 未跟踪,一般为 ...
- PHP的设计模式及场景应用介绍
有大量的文章解释什么是设计模式,如何实现设计模式,网络上不需要再写一篇这样的文章.相反,在本文中我们更多的讨论什么时候用和为什么要用,而不是用哪一个和如何使用. 我将会为这些设计模式描绘不同的场景和案 ...
- Java-第N篇推荐的一些学习书籍
1.推荐的一些学习书籍或者需要掌握的基本知识 book | |---ant | |---maven | |---git(菜鸟教程) | |---Dos shell | |---linux常用的命令.l ...
- docker添加加速器
通过 Docker 官方镜像加速,中国区用户能够快速访问最流行的 Docker 镜像.该镜像托管于中国大陆,本地用户现在将会享受到更快的下载速度和更强的稳定性,从而能够更敏捷地开发和交付 Docker ...
- [BZOJ2829] 信用卡 (凸包)
[BZOJ2829] 信用卡 (凸包) 题面 信用卡是一个矩形,唯四个角做了圆滑处理,使他们都是与矩形两边相切的1/4园,如下图所示,现在平面上有一些规格相同的信用卡,试求其凸包的周长.注意凸包未必是 ...
- ES6——generator
generator 生成器函数 普通函数,一路到底 generator函数,中间可以停,到哪停呢,用 yield 配合,交出执行权 yield 有 放弃.退让.退位的意思 需要调用next()方法启动 ...
- CSS利用border绘制图性
绘制梯形 width:10px; border-top:10px solid red; border-right:10px solid transparent; border-left:10px so ...