后缀数组+单调栈的应用

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

于是我们只需计算出即可

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

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

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

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

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

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

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

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

这样去更新就可以了

  1. #include <cstdio>
  2. #include <cmath>
  3. #include <cstring>
  4. #include <cstdlib>
  5. #include <iostream>
  6. #include <algorithm>
  7. #include <queue>
  8. #include <stack>
  9. #define ll long long
  10. using namespace std;
  11. char s[500005];
  12. int sa[500005];
  13. int rank[500005];
  14. int temprank[500005];
  15. int height[500005];
  16. int has[500005];
  17. int v[500005];
  18. int lx[500005],rx[500005];
  19. int l;
  20. bool be_same(int x,int y,int len)
  21. {
  22. return x+len>l||y+len>l||rank[x]!=rank[y]||rank[x+len]!=rank[y+len];
  23. }
  24. void get_sa()
  25. {
  26. int cnt=0;
  27. for(int i=1;i<=l;i++)v[i]=s[i];
  28. for(int i=1;i<=l;i++)has[v[i]]++;
  29. for(int i=0;i<128;i++)if(has[i])temprank[i]=++cnt;
  30. for(int i=1;i<128;i++)has[i]+=has[i-1];
  31. for(int i=1;i<=l;i++)
  32. {
  33. rank[i]=temprank[v[i]];
  34. sa[has[v[i]]--]=i;
  35. }
  36. for(int k=1;cnt!=l;k<<=1)
  37. {
  38. cnt=0;
  39. for(int i=1;i<=l;i++)has[i]=0;
  40. for(int i=1;i<=l;i++)has[rank[i]]++;
  41. for(int i=1;i<=l;i++)has[i]+=has[i-1];
  42. for(int i=l;i;i--)if(sa[i]>k)temprank[sa[i]-k]=has[rank[sa[i]-k]]--;
  43. for(int i=1;i<=k;i++)temprank[l-i+1]=has[rank[l-i+1]]--;
  44. for(int i=1;i<=l;i++)sa[temprank[i]]=i;
  45. for(int i=1;i<=l;i++)temprank[sa[i]]=be_same(sa[i],sa[i-1],k)?++cnt:cnt;
  46. for(int i=1;i<=l;i++)rank[i]=temprank[i];
  47. }
  48. for(int i=1;i<=l;i++)
  49. {
  50. if(rank[i]==1)continue;
  51. int j=max(1,height[rank[i-1]]-1);
  52. while(s[i+j-1]==s[sa[rank[i]-1]+j-1])height[rank[i]]=j++;
  53. }
  54. }
  55. void init()
  56. {
  57. height[0]=height[l+1]=-0x3f3f3f3f;
  58. ll ret=0;
  59. for(int i=1;i<=l;i++)lx[i]=i-1,rx[i]=i+1;
  60. for(int i=2;i<=l;i++)while(height[lx[i]]>height[i])lx[i]=lx[lx[i]];
  61. for(int i=l;i>=2;i--)while(height[rx[i]]>=height[i])rx[i]=rx[rx[i]];
  62. for(int i=2;i<=l;i++)ret+=2*height[i]*(ll)((ll)(rx[i]-i)*(ll)(i-lx[i]));
  63. ll ans=1ll*(l-1)*l/2ll*(l+1);
  64. printf("%lld\n",ans-ret);
  65. }
  66. int main()
  67. {
  68. scanf("%s",s+1);
  69. l=strlen(s+1);
  70. get_sa();
  71. init();
  72. return 0;
  73. }

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. java valueOf()函数

    valueOf() 方法用于返回给定参数的原生 Number 对象值,参数可以是原生数据类型, String等. 该方法是静态方法.该方法可以接收两个参数一个是字符串,一个是基数. 语法 该方法有以下 ...

  2. P1836 【数页码_NOI导刊2011提高(04)】

    P1836 数页码_NOI导刊2011提高(04) 题目描述 一本书的页码是从1—n编号的连续整数:1,2,3,…,n.请你求出全部页码中所有单个数字的和,例如第123页,它的和就是1+2+3=6. ...

  3. [模板] 匈牙利算法&&二分图最小字典序匹配

    匈牙利算法 简介 匈牙利算法是一种求二分图最大匹配的算法. 时间复杂度: 邻接表/前向星: \(O(n * m)\), 邻接矩阵: \(O(n^3)\). 空间复杂度: 邻接表/前向星: \(O(n ...

  4. Qt如何去掉按钮等控件的虚线框(焦点框)

    方法1:可以通过代码ui->pushButton->setFocusPolicy(Qt::NoFocus)或在Qt Creator的属性列表中设置. 方法2:如果在嵌入式设备中需要通过按键 ...

  5. Codeforces Global Round 2

    A:答案一定包含首位或末位. #include<iostream> #include<cstdio> #include<cmath> #include<cst ...

  6. Android greenDAO 数据库 简单学习之基本使用

    看网上对greenDAO介绍的不错,今天就动手来试一把,看看好不好使. greenDAO 官方网站:http://greendao-orm.com/ 代码托管地址:https://github.com ...

  7. IDEA 破解

    推荐三篇文章  : 1:    https://blog.csdn.net/nishiwodebocai21/article/details/71359619?fps=1&locationNu ...

  8. 导出python的环境

    1.导出 pip freeze >  packegas.txt 2.在其他环境安装 pip install -r  packages.txt

  9. Battery Historian 使用常用命令

    一.重置电池数据收集数据 打开电池数据获取:adb shell dumpsys batterystats --enable full-wake-history 重置电池数据: adb shell du ...

  10. Java异常知识整理_处理异常时的性能开销

    1.首先列两个从别的地方看到的说法: try-catch代码段会产生额外的性能开销,或者换个角度说,它往往会影响JVM对代码进行优化,所以建议仅捕获有必要的代码段,尽量不要一个大的try包住整段的代码 ...