51Nod 1600 Simple KMP

对于一个字符串\(|S|\),我们定义\(fail[i]\),表示最大的\(x\)使得\(S[1..x]=S[i-x+1..i]\),满足\((x<i)\)

显然对于一个字符串,如果我们将每个\(0\le i\le |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)\bmod 10^9+7\)


发现连的是KMP的fail边,然而这个并没有什么用处,考虑某个长\(n\)的串\(S\)的\(key(S)\)实际是什么

\[key(S)=\sum_{i=1}^n\sum_{j=i+1}^n(\sum_{l=i}^j\sum_{k=1}^{l-i}[S[i,i+k-1]=S[l-k+1,l]])
\]

前两个合式枚举子串,第三个枚举节点,第四个计算深度。

\[F(S)=\sum_{i=1}^n\sum_{k=1}^{n-i}[S[i,i+k-1]=S[n-k+1,n]]
\]

实际上计算的是钦定末尾位置,算前面的贡献。

可以得到

\[key(S)=\sum_{i=1}^n\sum_{j=1}^iF(S[1,j])
\]

然后对每个串,钦定每个可以成为末尾位置的\(j\),求和一下就成了。

如果算出了\(F[1,i]\),显然可以算出\(key\)

相等子串个数,考虑SAM

对于某个点\(i\),当被加入SAM的时候,对\(par\)树上的到根的链上的每个点都存在相等的串的匹配,考虑计算这条链的贡献。

链上的一个点的贡献可以被描述为\(cnt*le\),即在\([1,i-1]\)中出现的次数和这个点\(endpos\)所代表的集合的串长个数(就是\(len[x]-len[par[x]]\))

计算完贡献后,我们需要对这些点的\(cnt\)进行区间加\(1\)

直接用LCT或者树剖在最后离线维护即可。


Code:

#include <cstdio>
#include <cstring>
const int N=4e5+10;
const int mod=1e9+7;
inline void add(int &x,int y){x=x+y>=mod?x+y-mod:x+y;}
#define mul(x,y) (1ll*(x)*(y)%mod)
int n;
char s[N];
int len[N],par[N],tr[N][26],pos[N],tot=1,las=1;
void extend(int c)
{
int now=++tot,p=las;
len[now]=len[p]+1;
while(p&&!tr[p][c]) tr[p][c]=now,p=par[p];
if(!p) par[now]=1;
else
{
int x=tr[p][c];
if(len[x]==len[p]+1) par[now]=x;
else
{
int y=++tot;
len[y]=len[p]+1;
par[y]=par[x];
memcpy(tr[y],tr[x],sizeof tr[y]);
while(p&&tr[p][c]==x) tr[p][c]=y,p=par[p];
par[x]=par[now]=y;
}
}
las=now;
}
int ch[N][2],L[N],R[N],tag[N],sum[N],dat[N],lp[N],rp[N];
#define ls ch[now][0]
#define rs ch[now][1]
#define fa par[now]
bool isroot(int now){return ch[fa][0]==now||ch[fa][1]==now;}
int identity(int now){return ch[fa][1]==now;}
void connect(int f,int now,int typ){ch[fa=f][typ]=now;}
void updata(int now)
{
sum[now]=0;
add(sum[now],sum[ls]);
add(sum[now],dat[now]);
add(sum[now],sum[rs]);
L[now]=lp[now];
if(ls) L[now]=L[ls];
R[now]=rp[now];
if(rs) R[now]=R[rs];
}
void Rotate(int now)
{
int p=fa,typ=identity(now);
connect(p,ch[now][typ^1],typ);
if(isroot(p)) connect(par[p],now,identity(p));
else fa=par[p];
connect(now,p,typ^1);
updata(p),updata(now);
}
void upt(int now,int d)
{
add(tag[now],d);
add(dat[now],mul(d,rp[now]-lp[now]));
add(sum[now],mul(d,R[now]-L[now]));
}
void pushdown(int now)
{
if(tag[now])
{
if(ls) upt(ls,tag[now]);
if(rs) upt(rs,tag[now]);
tag[now]=0;
}
}
int sta[N],ct;
void splay(int now)
{
while(isroot(now)) sta[++ct]=now,now=fa;
sta[++ct]=now;
while(ct) pushdown(sta[ct--]);
now=sta[1];
for(;isroot(now);Rotate(now))
if(isroot(fa))
Rotate(identity(now)^identity(fa)?now:fa);
}
void access(int now)
{
for(int las=0;now;las=now,now=fa)
splay(now),rs=las,updata(now);
}
int qry(int now)
{
access(now),splay(now);
return sum[now];
}
void modi(int now)
{
access(now),splay(now);
upt(now,1);
}
int main()
{
scanf("%d%s",&n,s+1);
for(int i=1;i<=n;i++) extend(s[i]-'a'),pos[i]=las;
//for(int i=2;i<=tot;i++) printf("%d %d\n",par[i],i);
//for(int i=1;i<=n;i++) printf("%d ",pos[i]);puts("");
int ans=0,yuu=0;
for(int i=1;i<=tot;i++)
lp[i]=L[i]=len[par[i]],rp[i]=R[i]=len[i];
for(int i=1;i<=n;i++)
{
add(ans,qry(par[pos[i]]));
modi(pos[i]);
add(yuu,ans);
printf("%d\n",yuu);
}
return 0;
}

2019.5.3

51Nod 1600 Simple KMP 解题报告的更多相关文章

  1. 51Nod 1600 Simple KMP SAM+LCT/树链剖分

    1600 Simple KMP 对于一个字符串|S|,我们定义fail[i],表示最大的x使得S[1..x]=S[i-x+1..i],满足(x<i)显然对于一个字符串,如果我们将每个0<= ...

  2. 51nod 1600 Simple KMP【后缀自动机+LCT】【思维好题】*

    Description 对于一个字符串|S|,我们定义fail[i],表示最大的x使得S[1..x]=S[i-x+1..i],满足(x<i) 显然对于一个字符串,如果我们将每个0<=i&l ...

  3. 51nod 1600 Simple KMP

    又被机房神犇肉丝哥哥和glory踩爆了 首先这个答案的输出方式有点套路,当前的答案=上一个答案+每一个后缀的f值=上一个答案+上一次算的每个后缀的f值+当前每个后缀的深度 这个题意给了个根深度为-1有 ...

  4. codeforces B. Simple Molecules 解题报告

    题目链接:http://codeforces.com/problemset/problem/344/B 题目意思:这句话是解题的关键: The number of bonds of an atom i ...

  5. 51nod 算法马拉松17 解题报告 以后不能赛中写题解(查逐梦者抄袭本人代码...

    B题(数学题: 问(1+sqrt(2)) ^n  能否分解成 sqrt(m) +sqrt(m-1)的形式  如果可以 输出 m%1e9+7 否则 输出no n<=1e18 刚看题没思路 暴力一下 ...

  6. 北大ACM试题分类+部分解题报告链接

    转载请注明出处:優YoU http://blog.csdn.net/lyy289065406/article/details/6642573 部分解题报告添加新内容,除了原有的"大致题意&q ...

  7. ZROIDay3-比赛解题报告

    ZROIDay3-比赛解题报告 瞎扯 从今天开始考试有点不在状态,可能是因为不太适应题目的原因,T1已经接近了思想但是没有想到状态转移,T2思考方向错误,T3不会打LCT,还是太菜了 A 考场上想到要 ...

  8. 【LeetCode】227. Basic Calculator II 解题报告(Python)

    [LeetCode]227. Basic Calculator II 解题报告(Python) 标签(空格分隔): LeetCode 作者: 负雪明烛 id: fuxuemingzhu 个人博客: h ...

  9. CH Round #56 - 国庆节欢乐赛解题报告

    最近CH上的比赛很多,在此会全部写出解题报告,与大家交流一下解题方法与技巧. T1 魔幻森林 描述 Cortana来到了一片魔幻森林,这片森林可以被视作一个N*M的矩阵,矩阵中的每个位置上都长着一棵树 ...

随机推荐

  1. mysql 获取任意一个月的所有天数。

    SELECT ADDDATE(y.first, x.d - 1) as dFROM(SELECT 1 AS d UNION ALLSELECT 2 UNION ALLSELECT 3 UNION AL ...

  2. BZOJ 4059: [Cerc2012]Non-boring sequences(启发式分治)

    传送门 解题思路 首先可以想到要预处理一个\(nxt_i\)和\(pre_i\),表示前后与当前位置权值相同的节点,那么这样可以迅速算出某个点在某段区间是否出现多次.然后这样的话就考虑分治,对于\([ ...

  3. (转)Android OpenGL ES(一)

    转:http://wiki.jikexueyuan.com/project/opengl-es-guide/pipeline.html OpenGL ES 主要用来开发 3D 图形应用的.OpenGL ...

  4. python笔试做错的题目

    a = [1,2,3] b = a print(id(a),id(b),a == b) print(a,b) b = b + [1,2,3] print(a,b) print(id(a),id(b), ...

  5. HTML5: HTML5 应用程序缓存

    ylbtech-HTML5: HTML5 应用程序缓存 1.返回顶部 1. HTML5 应用程序缓存 使用 HTML5,通过创建 cache manifest 文件,可以轻松地创建 web 应用的离线 ...

  6. HTML5: HTML5 Web SQL 数据库

    ylbtech-HTML5: HTML5 Web SQL 数据库 1.返回顶部 1. HTML5 Web SQL 数据库 Web SQL 数据库 API 并不是 HTML5 规范的一部分,但是它是一个 ...

  7. ADO方式,VC调用Execute执行INSERT INTO插入变量SQL语句的写法

    ADO方式,VC调用Execute执行INSERT INTO插入变量SQL语句的写法 有些情况下,SQL SERVER 2008r2中需要保存float,int类型的数据,当C 中的变量为double ...

  8. sqlserver定时作业,定时执行存储过程

    首先,我想说,我真的是渣了,一个这个玩意弄了半天,算了,直接切入正题吧. 第一步: 先写好存储过程 用了两张表,你们自己建立吧 <br data-filtered="filtered& ...

  9. bootstrap Grid布局(网格布局)

    基本网络结构 <div class="container"> <div class="row"> <div class=" ...

  10. PAT甲级——A1153 DecodeRegistrationCardofPAT【25】

    A registration card number of PAT consists of 4 parts: the 1st letter represents the test level, nam ...