后缀数组+单调栈的应用

首先我们研究一下这个表达式,可以发现前半部分与串的情况并没有关系,而只是跟串的长度有关,所以我们先把前半部分算出来:

于是我们只需计算出即可

那么可以发现,对于排名分别为i,j的两个串,他们的lcp应当是:

但是这里的时间复杂度仍然很大

我们换一个角度来思考:如果设,那么我们认为height[k]产生了一个贡献

所以我们可以从每一个height[k]产生了多少贡献的角度来思考,这样就可以把时间复杂度降到O(n)

不难发现,一个k会对一个区间产生贡献的条件就是height[k]是所在区间的最小值

这就可以用单调栈维护了!!

但是要注意,为了防止重复计算,我们对单调栈的两端点的取等条件设成不一样的(即左侧算到第一个height小于等于height[k],右侧算到第一个height小于height[k]的位置)

这样找到每个点向左和向右能延伸的位置lx,rx这样他所占的区间个数就是(i-lx)*(rx-i)

这样去更新就可以了

#include <cstdio>
#include <cmath>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#include <queue>
#include <stack>
#define ll long long
using namespace std;
char s[500005];
int sa[500005];
int rank[500005];
int temprank[500005];
int height[500005];
int has[500005];
int v[500005];
int lx[500005],rx[500005];
int l;
bool be_same(int x,int y,int len)
{
return x+len>l||y+len>l||rank[x]!=rank[y]||rank[x+len]!=rank[y+len];
}
void get_sa()
{
int cnt=0;
for(int i=1;i<=l;i++)v[i]=s[i];
for(int i=1;i<=l;i++)has[v[i]]++;
for(int i=0;i<128;i++)if(has[i])temprank[i]=++cnt;
for(int i=1;i<128;i++)has[i]+=has[i-1];
for(int i=1;i<=l;i++)
{
rank[i]=temprank[v[i]];
sa[has[v[i]]--]=i;
}
for(int k=1;cnt!=l;k<<=1)
{
cnt=0;
for(int i=1;i<=l;i++)has[i]=0;
for(int i=1;i<=l;i++)has[rank[i]]++;
for(int i=1;i<=l;i++)has[i]+=has[i-1];
for(int i=l;i;i--)if(sa[i]>k)temprank[sa[i]-k]=has[rank[sa[i]-k]]--;
for(int i=1;i<=k;i++)temprank[l-i+1]=has[rank[l-i+1]]--;
for(int i=1;i<=l;i++)sa[temprank[i]]=i;
for(int i=1;i<=l;i++)temprank[sa[i]]=be_same(sa[i],sa[i-1],k)?++cnt:cnt;
for(int i=1;i<=l;i++)rank[i]=temprank[i];
}
for(int i=1;i<=l;i++)
{
if(rank[i]==1)continue;
int j=max(1,height[rank[i-1]]-1);
while(s[i+j-1]==s[sa[rank[i]-1]+j-1])height[rank[i]]=j++;
}
}
void init()
{
height[0]=height[l+1]=-0x3f3f3f3f;
ll ret=0;
for(int i=1;i<=l;i++)lx[i]=i-1,rx[i]=i+1;
for(int i=2;i<=l;i++)while(height[lx[i]]>height[i])lx[i]=lx[lx[i]];
for(int i=l;i>=2;i--)while(height[rx[i]]>=height[i])rx[i]=rx[rx[i]];
for(int i=2;i<=l;i++)ret+=2*height[i]*(ll)((ll)(rx[i]-i)*(ll)(i-lx[i]));
ll ans=1ll*(l-1)*l/2ll*(l+1);
printf("%lld\n",ans-ret);
}
int main()
{
scanf("%s",s+1);
l=strlen(s+1);
get_sa();
init();
return 0;
}

bzoj 3238的更多相关文章

  1. [BZOJ 3238] [AHOI 2013] 差异 【后缀数组 + 单调栈】

    题目链接:BZOJ - 3238 题目分析 显然,这道题就是求任意两个后缀之间的LCP的和,这与后缀数组的联系十分明显. 求出后缀数组后,求出字典序相邻两个后缀的LCP,即 Height 数组. 那么 ...

  2. BZOJ 3238 差异

    BZOJ 3238 差异 看这个式子其实就是求任意两个后缀的 $ LCP $ 长度和.前面的 $ len(T_i)+len(T_j) $ 求和其实就是 $ n(n-1)(n+1)/2 $ ,这个是很好 ...

  3. BZOJ 3238: [Ahoi2013]差异 [后缀数组 单调栈]

    3238: [Ahoi2013]差异 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 2326  Solved: 1054[Submit][Status ...

  4. bzoj 3238 Ahoi2013 差异

    3238: [Ahoi2013]差异 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 2357  Solved: 1067[Submit][Status ...

  5. BZOJ 3238: [Ahoi2013]差异 [后缀自动机]

    3238: [Ahoi2013]差异 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 2512  Solved: 1140[Submit][Status ...

  6. ●BZOJ 3238 [Ahoi2013]差异

    题链: http://www.lydsy.com/JudgeOnline/problem.php?id=3238 题解: 后缀数组套路深. 问题转化为求出任意两个后缀的LCP之和 在计算贡献时,各种不 ...

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

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

  8. 【BZOJ 3238】 3238: [Ahoi2013]差异(SAM)

    3238: [Ahoi2013]差异 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 3047  Solved: 1375 Description In ...

  9. BZOJ 3238: [Ahoi2013]差异 后缀自动机 树形dp

    http://www.lydsy.com/JudgeOnline/problem.php?id=3238 就算是全局变量,也不要忘记,初始化(吐血). 长得一副lca样,没想到是个树形dp(小丫头还有 ...

  10. bzoj 3238: [Ahoi2013]差异 -- 后缀数组

    3238: [Ahoi2013]差异 Time Limit: 20 Sec  Memory Limit: 512 MB Description Input 一行,一个字符串S Output 一行,一个 ...

随机推荐

  1. Crypto加密解密

    crypto 模块提供了加密功能,包含对 OpenSSL 的哈希.HMAC.加密.解密.签名.以及验证功能的一整套封装.我们这里讲crypto AES算法加密 一.使用步骤 1.引入Crypto 1. ...

  2. Delphi Create(nil), Create(self), Create(Application)的区别

    最近的项目中经常在程序中动态创建控件,势必用到Create. 但是随之而来的问题就是动态创建的控件是否可以正确的释放内存? 以及 Create(nil), Create(self), Create(A ...

  3. python之路3-元组、列表、字典、集合

    1.元组 特点:一旦创建,内容不可修改,又叫只读列表 a= ('wang','zhang','zhao') print(a.count('zhao')) print(a.index('wang')) ...

  4. 自定义组件的properties和data

    // components/epsoide/index.js Component({ /** * 组件的属性列表 */ properties: { index: { type: Number //va ...

  5. 如何破解加密了的word文档

    https://blog.csdn.net/huangbaokang/article/details/79630223 变成xml文件格式之后--查找在documentProtection前面加上un ...

  6. Vivado寄存器初始值问题

    前言 本复位只针对Vivado中的寄存器复位. 什么时候需要复位?到底要不要复位?怎么复位?复位有什么卵用? 该复位的寄存器需要复位,复位使得寄存器恢复初始值,有的寄存器并不需要复位(数据流路径上). ...

  7. 05VueCli

    详情请见: https://pizzali.github.io/2018/10/30/VueCli/ Vue CLI 是一个基于 Vue.js 进行快速开发的完整系统,Vue CLI 致力于将 Vue ...

  8. mysql日志分析工具之mysqlsla

    背景介绍: 很多情况下,都需要对MySQL日志进行各种分析,来了解系统运行的方方面面.MySQL官方自带了一些工具对日志进行分析,比如mysqlbinlog可以用来分析二进制日志,mysqlslow可 ...

  9. windows telnet 模拟 http请求

    1. 开启windows自带的telnet客户端(控制面板 --> 程序 --> 启用或关闭windows功能 --> ) 2. 打开cmd,使用Telnet客户端 3. 按ctrl ...

  10. AngularJS DI(依赖注入)实现推测

    AngularJS DI(依赖注入) http://www.cnblogs.com/whitewolf/archive/2012/09/11/2680659.html 回到angularjs:在框架中 ...