关于解法这个讲的很清楚了,主要用了设关键点的巧妙思想。

主要想说的是一个刚学的方法:通过后缀自动机建立后缀树,再转成后缀数组。

后缀数组功能强大,但是最令人头疼的地方是模板太难背容易写错。用这个方法,只需要用上SAM的模板即可。

https://blog.csdn.net/lvzelong2014/article/details/79006541

反串后缀自动机的parent树就是原串的后缀树,一遍DFS即可求出后缀数组。

这样代码复杂度上可能稍简单些(在忘记SA模板的时候可以用),构建过程的复杂度也由$O(n\log n)$变为线性,但由于这个线性复杂度是非常满的,所以常常会比SA还要慢不少。注意SAM的数组最好开两倍,一倍是肯定不够的。

  1. #include<cstdio>
  2. #include<cstring>
  3. #include<algorithm>
  4. #define mem(a) memset(a,0,sizeof(a))
  5. #define LCP(x,y) SA.que(x,y)
  6. #define LCS(x,y) SA1.que(n-y+1,n-x+1)
  7. #define rep(i,l,r) for (int i=(l); i<=(r); i++)
  8. typedef long long ll;
  9. using namespace std;
  10.  
  11. const int N=;
  12. int n,T,f[N],g[N],log[N];
  13. char s[N]; ll ans;
  14.  
  15. struct Suffix{
  16. int lst,cnt,np,tot,h[N],pos[N],x[N],b[N],mx[N];
  17. int son[N][],fa[N],ch[N][],rk[N],sa[N],st[N][];
  18. void init(){ lst=cnt=; tot=; mem(b); mem(ch); mem(fa); mem(son); }
  19.  
  20. void ext(int c,int k){
  21. int p=lst; lst=np=++cnt; pos[np]=k; b[np]=; mx[np]=mx[p]+;
  22. while (p && !son[p][c]) son[p][c]=np,p=fa[p];
  23. if (!p) fa[np]=;
  24. else{
  25. int q=son[p][c];
  26. if (mx[q]==mx[p]+) fa[np]=q;
  27. else{
  28. int nq=++cnt; mx[nq]=mx[p]+; pos[nq]=pos[q];
  29. while (p && son[p][c]==q) son[p][c]=nq,p=fa[p];
  30. memcpy(son[nq],son[q],sizeof(son[nq]));
  31. fa[nq]=fa[q]; fa[q]=fa[np]=nq;
  32. }
  33. }
  34. }
  35.  
  36. void build(){ rep(i,,cnt) ch[fa[i]][x[pos[i]+mx[fa[i]]]]=i; }
  37. void dfs(int x){
  38. if (b[x]) sa[rk[pos[x]]=++tot]=pos[x];
  39. rep(i,,) if (ch[x][i]) dfs(ch[x][i]);
  40. }
  41.  
  42. void get(){
  43. int k=;
  44. rep(i,,n){
  45. for (int j=sa[rk[i]-]; i+k<=n && j+k<=n && x[i+k]==x[j+k]; k++);
  46. h[rk[i]]=k; if (k) k--;
  47. }
  48. }
  49.  
  50. void rmq(){
  51. rep(i,,n) st[i][]=h[i];
  52. rep(i,,log[n])
  53. rep(j,,n-(<<i)+) st[j][i]=min(st[j][i-],st[j+(<<(i-))][i-]);
  54. }
  55.  
  56. int ask(int l,int r){
  57. l++; int t=log[r-l+];
  58. return min(st[l][t],st[r-(<<t)+][t]);
  59. }
  60.  
  61. int que(int x,int y){ return ask(min(rk[x],rk[y]),max(rk[x],rk[y]));}
  62.  
  63. void build_sa(char s[]){
  64. for (int i=n; i; i--) ext(x[i]=s[i]-'a'+,i);
  65. build(); dfs(); get(); rmq();
  66. }
  67. }SA,SA1;
  68.  
  69. void solve(){
  70. mem(f); mem(g);
  71. for (int len=,x,y,l,r; *len<=n; len++)
  72. for (int i=,j=len+; j<=n; i+=len,j+=len)
  73. if (s[i]==s[j]){
  74. x=LCS(i,j); y=LCP(i,j);
  75. l=max(i,i-x+len); r=min(i+y,j);
  76. if (r>l){
  77. f[l+len]++; f[r+len]--;
  78. g[l-len+]++; g[r-len+]--;
  79. }
  80. }
  81. rep(i,,n) f[i]+=f[i-],g[i]+=g[i-];
  82. rep(i,,n-) ans+=(ll)f[i]*g[i+];
  83. }
  84.  
  85. int main(){
  86. freopen("bzoj4650.in","r",stdin);
  87. freopen("bzoj4650.out","w",stdout);
  88. log[]=; rep(i,,N) log[i]=log[i>>]+;
  89. scanf("%d",&T);
  90. while (T--){
  91. SA.init(); SA1.init(); scanf("%s",s+); n=strlen(s+);
  92. SA.build_sa(s); reverse(s+,s+n+);
  93. SA1.build_sa(s); reverse(s+,s+n+);
  94. ans=; solve(); printf("%lld\n",ans);
  95. }
  96. return ;
  97. }

[BZOJ4650][NOI2016]优秀的拆分(SAM构建SA)的更多相关文章

  1. [UOJ#219][BZOJ4650][Noi2016]优秀的拆分

    [UOJ#219][BZOJ4650][Noi2016]优秀的拆分 试题描述 如果一个字符串可以被拆分为 AABBAABB 的形式,其中 A 和 B 是任意非空字符串,则我们称该字符串的这种拆分是优秀 ...

  2. BZOJ4650 [NOI2016]优秀的拆分 【后缀数组】

    题目 如果一个字符串可以被拆分为 AABBAABB 的形式,其中 AA 和 BB 是任意非空字符串,则我们称该字符串的这种拆 分是优秀的.例如,对于字符串 aabaabaa,如果令 A=aabA=aa ...

  3. UOJ#219/BZOJ4650 [NOI2016]优秀的拆分 字符串 SA ST表

    原文链接http://www.cnblogs.com/zhouzhendong/p/9025092.html 题目传送门 - UOJ#219 (推荐,题面清晰) 题目传送门 - BZOJ4650 题意 ...

  4. BZOJ4650 NOI2016优秀的拆分(后缀数组)

    显然只要求出以每个位置开始的AA串数量就可以了,将其和反串同位置的结果乘一下,加起来就是答案.考虑对每种长度的字符串计数.若当前考虑的A串长度为x,我们每隔x个字符设一个关键点,求出相邻两关键点的后缀 ...

  5. bzoj千题计划317:bzoj4650: [Noi2016]优秀的拆分(后缀数组+差分)

    https://www.lydsy.com/JudgeOnline/problem.php?id=4650 如果能够预处理出 suf[i] 以i结尾的形式为AA的子串个数 pre[i] 以i开头的形式 ...

  6. 题解【bzoj4650 [NOI2016]优秀的拆分】

    Description 求对每一个连续字串将它切割成形如 AABB 的形式的方案数之和 Solution 显然 AABB 是由两个 AA 串拼起来的 考虑维护两个数组 a[i] 和 b[i] ,其中 ...

  7. BZOJ4650: [Noi2016]优秀的拆分

    考场上没秒的话多拿5分并不划算的样子. 思想其实很简单嘛. 要统计答案,求以每个位置开始和结束的AA串数量就好了.那么枚举AA中A的长度L,每L个字符设一个关键点,这样AA一定经过相邻的两个关键点.计 ...

  8. BZOJ4650: [Noi2016]优秀的拆分(hash 调和级数)

    题意 题目链接 Sol NOI的题都这么良心么.. 先交个\(n^4\)暴力 => 75 hash优化一下 => 90 然后\(90\)到\(100\)分之间至少差了\(10\)难度台阶= ...

  9. bzoj4650: [Noi2016]优秀的拆分 hash

    好气啊,没开longlong又biubiu了 底层: 用hash或者奇奇怪怪的算法兹磁logn求最长公共前后缀 思路: 统计出从一个点开始和结束的形如AA的子串的个数 统计的时候把相邻的结果相乘加起来 ...

随机推荐

  1. ng websocket

    ng使用websocket 1.安装依赖库npm install ws --save 2.安装类型定义文件 npm install @types/ws --save 3.编写服务 import { I ...

  2. powershell for rename server name

    Rename server name if server has not yet joined AD $Hostname = "newname" $username = " ...

  3. ActiveMQ(4) ActiveMQ JDBC 持久化 Mysql 数据库

    ActiveMQ 消息持久化机制: ActiveMQ 消息的持久化机制有 JDBC.AMQ.KahaDB 和 LevelDB,其中本示例版本(5.15.2)默认机制为 KahaDB.无论哪种持久化机制 ...

  4. Spring学习--HelloWorld

    Spring: Spring 是一个开源框架. Spring 是为简化企业级应用开发而生,使用 Spring 可以使简单的 JavaBean 实现以前只有 EJB 才能实现的功能. Spring 是一 ...

  5. DOM 2

    1.对文档的信息进行检索常用的方法: getElementById; getElementsByTagName; getAttribute;//得到的是属性值 2把需要的信息添加到DOM中常用的方法: ...

  6. input输入浮动提示

    html代码: <!DOCTYPE html> <html lang="en"> <head> <meta charset="U ...

  7. Spring 学习笔记(三)之注解

    一.在classpath下扫描组件 •组件扫描(component scanning):  Spring 能够从 classpath 下自动扫描, 侦测和实例化具有特定注解的组件. •特定组件包括: ...

  8. 【Foreign】朗格拉日计数 [暴力]

    朗格拉日计算 Time Limit: 10 Sec  Memory Limit: 128 MB Description Input Output 仅一行一个整数表示答案. Sample Input 5 ...

  9. Python 本地线程

    1. 本地线程,保证即使是多个线程,自己的值也是互相隔离. 2.普通对象演示 import threading import time class A(): pass a=A() def func(n ...

  10. Linux c括号作用域【原创笔记】

    大师指点后,所做的笔记,很感谢一起愿意研究技术的同事,以下不是本人原创,是他分析的成果 #include <stdio.h> #include <time.h> struct ...