可能是一个 SAM 常用技巧?感觉 SAM 的基础题好多啊..

题目描述

给定一个长度为 \(n\) 的字符串 \(S\) ,令 \(T_i\) 表示它从第 \(i\) 个字符开始的后缀,求:

\[\sum_{1\le i<j\le n}len(T_i)+len(T_j)-2\times lcp(T_i,T_j)
\]

其中,\(len(a)\) 表示字符串 \(a\) 的长度,\(lcp(a,b)\) 表示字符串 \(a\) 和字符串 \(b\) 的最长公共前缀。

输入输出格式

输入格式:

一行,一个字符串 \(S\)。

输出格式:

一行,一个整数,表示所求值。

输入输出样例

输入样例:

ababc

输出样例:

54

数据范围与约定

对于 \(100\%\) 的数据,保证 \(2\le n\le 500000\),且均为小写字母。

题解:

这个题的主要考点是 SAM 求 lcp。

lcp 是最长公共前缀,而我们用的是后缀自动机。只需要把字符串翻转过来建立 SAM,得到的就是”前缀自动机“了。

类似后缀自动机的性质,我们建造 parent 树,则父亲总是儿子的前缀。而最简状态自动机上的每个点都有自己的 \(mn,mx\) 表示同一个 Right 集合能表示的最短和最长子串,因此任意两个状态的 lcp 长度就是它们在 parent 树上的最近公共祖先节点的 \(mx\)。

然后我们只需要计算任意两点的 lcp 即可,这个操作可以一次 dfs 搞定。注意统计时子树的根也要算上。

每个点做出的贡献是它的 Right 集合大小,可以通过在 parent 树上统计子树信息得到。

此外,前面的 \(\sum_{1\le i<j\le n}i+j\) 可以推导出来 \(O(n)\) 或 \(O(1)\) 求出来。

时间复杂度 \(O(n)​\)。

Code:

#include<cstdio>
#include<cstring>
#include<algorithm>
struct edge
{
int n,nxt;
edge(int n,int nxt)
{
this->n=n;
this->nxt=nxt;
}
edge(){}
}e[1000100];
int head[1000100],ecnt=-1;
void add(int from,int to)
{
e[++ecnt]=edge(to,head[from]);
head[from]=ecnt;
}
int n;
char s[500100];
int ch[26][1000100],mx[1000100],r[1000100],par[1000100],pcnt;
void build()
{
int p=pcnt=1;
for(int i=1;i<=n;++i)
{
int w=s[i]-'a';
int np=++pcnt;
mx[np]=mx[p]+1;
r[np]=1;
while(p&&!ch[w][p])
{
ch[w][p]=np;
p=par[p];
}
if(!p)
par[np]=1;
else
{
int q=ch[w][p];
if(mx[q]==mx[p]+1)
par[np]=q;
else
{
int nq=++pcnt;
mx[nq]=mx[p]+1;
while(p&&ch[w][p]==q)
{
ch[w][p]=nq;
p=par[p];
}
for(int j=0;j<26;++j)
ch[j][nq]=ch[j][q];
par[nq]=par[q];
par[q]=nq;
par[np]=nq;
}
}
p=np;
}
}
long long ans=0;
void dfs(int x)
{
long long tmp=r[x];
for(int i=head[x];~i;i=e[i].nxt)
{
dfs(e[i].n);
ans-=tmp*mx[x]*r[e[i].n]*2;
r[x]+=r[e[i].n];
tmp+=r[e[i].n];
}
}
int main()
{
memset(head,-1,sizeof(head));
scanf("%s",s+1);
n=strlen(s+1);
for(int i=1;i<=n;++i)
ans+=3ll*i*(i-1);
ans>>=1;
std::reverse(s+1,s+1+n);
build();
for(int i=2;i<=pcnt;++i)
add(par[i],i);
dfs(1);
printf("%lld\n",ans);
return 0;
}

洛谷 P4248 / loj 2377 [AHOI2013] 差异 题解【后缀自动机】【树形DP】的更多相关文章

  1. 洛谷 P4248: bzoj 3238: [AHOI2013]差异

    题目传送门:洛谷 P4248. 题意简述: 定义两个字符串 \(S\) 和 \(T\) 的差异 \(\operatorname{diff}(S,T)\) 为这两个串的长度之和减去两倍的这两个串的最长公 ...

  2. BZOJ_3238_[Ahoi2013]差异_后缀自动机

    BZOJ_3238_[Ahoi2013]差异_后缀自动机 Description Input 一行,一个字符串S Output 一行,一个整数,表示所求值 Sample Input cacao Sam ...

  3. 洛谷 P3975 / loj 2102 [TJOI2015] 弦论 题解【后缀自动机】【拓扑排序】

    后缀自动机入门. 题目描述 为了提高智商,ZJY 开始学习弦论. 这一天,她在<String theory>中看到了这样一道问题:对于一个给定的长度为 \(n\) 的字符串,求出它的第 \ ...

  4. 洛谷AT2046 Namori(思维,基环树,树形DP)

    洛谷题目传送门 神仙思维题还是要写点东西才好. 树 每次操作把相邻且同色的点反色,直接这样思考会发现状态有很强的后效性,没办法考虑转移. 因为树是二分图,所以我们转化模型:在树的奇数层的所有点上都有一 ...

  5. 洛谷P4493 [HAOI2018]字串覆盖(后缀自动机+线段树+倍增)

    题面 传送门 题解 字符串就硬是要和数据结构结合在一起么--\(loj\)上\(rk1\)好像码了\(10k\)的样子-- 我们设\(L=r-l+1\) 首先可以发现对于\(T\)串一定是从左到右,能 ...

  6. BZOJ 3238 [Ahoi2013]差异(后缀自动机)

    [题目链接] http://www.lydsy.com/JudgeOnline/problem.php?id=3238 [题目大意] 给出一个串,设T[i]表示从第i位开始的后缀, 求sum(len( ...

  7. 洛谷P4770 [NOI2018]你的名字(后缀自动机+线段树)

    传送门 我有种自己根本没学过SAM的感觉……最后还是抄了老半天的题解…… 首先,对$S$和每一次的$T$都建一个SAM 先考虑一下$l=1,r=\left| S \right|$的情况 设$lim_i ...

  8. 【洛谷 P4248】 [AHOI2013]差异(后缀自动机)

    题目链接 \[ans=\sum_{1<=i<j<=n}len(T_i)+len(T_j)-2*lcp(T_i,T_j)\] 观察这个式子可以发现,前面两个\(len\)是常数,后面的 ...

  9. BZOJ3238: [Ahoi2013]差异 (后缀自动机)

    Description Input 一行,一个字符串S Output 一行,一个整数,表示所求值 Sample Input cacao Sample Output 54 HINT 2<=N< ...

随机推荐

  1. 基于redis实现分布式Session

    学习到好的知识还是需要记录下来的. 开发环境 asp.net mvc4,iis.asp.net 自带的session机制存在诸多不好的地方.先只要列出几点. asp.net mvc 默认的sessio ...

  2. HaXe以及OpenFL部署

    HaXe以及OpenFL部署 Haxe是一种跨平台的编程语言,本文并未HAXE的教程,只是针对OPENFL以及HAXE的部署教程.HAXE的语法非常类似AS3,由于国内部署HAXE艰难,经常下载到一半 ...

  3. myeclipse快捷键记忆

    提示 Alt+?自动排版 Ctrl+shift+f自动添加引入包 Ctrl+shift+O切换窗口 Ctrl+F6自动添加set get方法 Alt+shift+s r 查看都是哪里调用了该方法 Ct ...

  4. spring mvc---controller返回值

    1.ModelAndView 类似servlet,之前例子在用 addObject setViewName 2.String a.表示返回逻辑视图名 spring mvc的视图解析器中设置.(在web ...

  5. CSS 学习-文本 段落

    段落. 首行缩进 text-indent属性 比如缩进  <p style="text-indent: 2em;">这里是内容....</p> 这里是内容这 ...

  6. [教学] Log.d 日志调试查看(所有平台)

    Firemonkey 提供了一个跨平台的日志显示函数 Log.d,当 App 越来越大 Debug 编译越来越慢时,可以利用它在 Release 模式来除错,下列说明如何在各平台查看. 小技巧:可以在 ...

  7. log4net 入门使用

    log4net 是dotnet平台下的一个日记记录组件. 一  NuGet中安装log4net包: 二 配置log4net.config文件 配置文件内容: <?xml version=&quo ...

  8. Android-ContentProvider读取/新增/操作系统联系人数据

    想要访问Android操作系统的ContentProvider就需要明白以下原理: 在Android操作系统里面的 /packsges/目录: apps: 很多的系统应用,例如:联系人,浏览器,音乐播 ...

  9. sudo -s/sodo -i/su root

    sudo : 暂时切换到超级用户模式以执行超级用户权限,提示输入密码时该密码为当前用户的密码,而不是超级账户的密码.不过有时间限制,Ubuntu默认为一次时长15分钟.su : 切换到某某用户模式,提 ...

  10. touch和click优先性

    jQuery的touch事件是当用户触摸事件(页面)时触发的. jQuery的click事件是当用户点击元素时触发的. 而事件执行流程是手指点击一个元素,会经过:touchstart --> t ...