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. 云数据库POLARDB产品解读之二:如何做到高性价比

    现在做任何事情都要看投入产出比,对应到数据库上其实就是性价比.POLARDB作为一款阿里自研数据库,经常被问的问题是:性能怎么样?能不能支撑我的业务?价格贵不贵?很显然,在早期调研阶段,对稳定性.可靠 ...

  2. [CSP-S模拟测试]:赤(red)(WQS二分+DP)

    题目传送门(内部题38) 输入格式 每个输入文件包含多组测试数据.选手应当处理到文件结束($EOF$) 每一组数据包括$3$行. 第$1$行包含三个正整数$n,a,b$,表示有$n$只猫,$gyz$有 ...

  3. HTML CSS + DIV实现排版布局

    HTML CSS + DIV实现排版布局 1.网页可以看成是由一个一个"盒子"组成,如图: 由上图可以看出,页面分为上(网站导航).中.下(版权声明)三个部分,中间部分又分为左(商 ...

  4. jsp中jstl、el使用

    tomcat7.0+JSTL1.1.2(不冲突) EL表达式获取变量 ${表达式} 如:${user.name} 不可以动态取值 ${user[name]}可以动态取值,变量名中含有特殊字符时只能用此 ...

  5. eclipse 集成 STS 插件

    eclipse 集成 STS 插件 想新建一个 Spring Boot 工程,发现没有,如图:(展示的是集成之后的) eclipse 要和 sts 版本对应的,进入http://spring.io/t ...

  6. 安卓真机或者模拟器运行安装应用时提示 Failure [INSTALL_FAILED_NO_MATCHING_ABIS: Failed to extract native libraries, res=-113]解决办法

    有时候为了方便调试APP,会在电脑上开启模拟器来调试我们的代码,有时候会出现 Failure [INSTALL_FAILED_NO_MATCHING_ABIS: Failed to extract n ...

  7. java遇到的问题

    1.java 初始化泛型数组 public static <T> T[] toArray(java.util.List<T> src, Class<T> type) ...

  8. VS2008中所有快捷键

    转载自:http://itfocus.diandian.com/post/2011-09-16/5091994 微软开发环境的可视化界面做的很全面,几乎所有的操作都可以通过可视化界面完成,但是你是否在 ...

  9. 使用Nuget重新安装packages.config中的组件的方法

    Update-Package -ProjectName 'Ko.app.web' -Reinstall 该语句作用:按照packages.config中给出的程序组件,重新下载安装一遍.

  10. 回头看看HTML5

    前言:自从学习各种框架各种成熟的控件库,越来越觉得疲惫. 一.用语义元素构造网页 在html5中最常用到的页面结构相关的语义元素如下: 页面结构想相关的语义元素 元素 说明 <article&g ...