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

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

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

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

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

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

 #include<cstdio>
#include<cstring>
#include<algorithm>
#define mem(a) memset(a,0,sizeof(a))
#define LCP(x,y) SA.que(x,y)
#define LCS(x,y) SA1.que(n-y+1,n-x+1)
#define rep(i,l,r) for (int i=(l); i<=(r); i++)
typedef long long ll;
using namespace std; const int N=;
int n,T,f[N],g[N],log[N];
char s[N]; ll ans; struct Suffix{
int lst,cnt,np,tot,h[N],pos[N],x[N],b[N],mx[N];
int son[N][],fa[N],ch[N][],rk[N],sa[N],st[N][];
void init(){ lst=cnt=; tot=; mem(b); mem(ch); mem(fa); mem(son); } void ext(int c,int k){
int p=lst; lst=np=++cnt; pos[np]=k; b[np]=; mx[np]=mx[p]+;
while (p && !son[p][c]) son[p][c]=np,p=fa[p];
if (!p) fa[np]=;
else{
int q=son[p][c];
if (mx[q]==mx[p]+) fa[np]=q;
else{
int nq=++cnt; mx[nq]=mx[p]+; pos[nq]=pos[q];
while (p && son[p][c]==q) son[p][c]=nq,p=fa[p];
memcpy(son[nq],son[q],sizeof(son[nq]));
fa[nq]=fa[q]; fa[q]=fa[np]=nq;
}
}
} void build(){ rep(i,,cnt) ch[fa[i]][x[pos[i]+mx[fa[i]]]]=i; }
void dfs(int x){
if (b[x]) sa[rk[pos[x]]=++tot]=pos[x];
rep(i,,) if (ch[x][i]) dfs(ch[x][i]);
} void get(){
int k=;
rep(i,,n){
for (int j=sa[rk[i]-]; i+k<=n && j+k<=n && x[i+k]==x[j+k]; k++);
h[rk[i]]=k; if (k) k--;
}
} void rmq(){
rep(i,,n) st[i][]=h[i];
rep(i,,log[n])
rep(j,,n-(<<i)+) st[j][i]=min(st[j][i-],st[j+(<<(i-))][i-]);
} int ask(int l,int r){
l++; int t=log[r-l+];
return min(st[l][t],st[r-(<<t)+][t]);
} int que(int x,int y){ return ask(min(rk[x],rk[y]),max(rk[x],rk[y]));} void build_sa(char s[]){
for (int i=n; i; i--) ext(x[i]=s[i]-'a'+,i);
build(); dfs(); get(); rmq();
}
}SA,SA1; void solve(){
mem(f); mem(g);
for (int len=,x,y,l,r; *len<=n; len++)
for (int i=,j=len+; j<=n; i+=len,j+=len)
if (s[i]==s[j]){
x=LCS(i,j); y=LCP(i,j);
l=max(i,i-x+len); r=min(i+y,j);
if (r>l){
f[l+len]++; f[r+len]--;
g[l-len+]++; g[r-len+]--;
}
}
rep(i,,n) f[i]+=f[i-],g[i]+=g[i-];
rep(i,,n-) ans+=(ll)f[i]*g[i+];
} int main(){
freopen("bzoj4650.in","r",stdin);
freopen("bzoj4650.out","w",stdout);
log[]=; rep(i,,N) log[i]=log[i>>]+;
scanf("%d",&T);
while (T--){
SA.init(); SA1.init(); scanf("%s",s+); n=strlen(s+);
SA.build_sa(s); reverse(s+,s+n+);
SA1.build_sa(s); reverse(s+,s+n+);
ans=; solve(); printf("%lld\n",ans);
}
return ;
}

[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. CENSORING

    CENSORING 题目描述 FJ为它的奶牛订阅了很多杂志,balabala.......,其中有一些奶牛不宜的东西(比如如何煮牛排). FJ将杂志中所有的文章提取出来组成一个长度最多为10^5的字符 ...

  2. 【BZOJ 3123】 [Sdoi2013]森林 主席树启发式合并

    我们直接按父子关系建主席树,然后记录倍增方便以后求LCA,同时用并查集维护根节点,而且还要记录根节点对应的size,用来对其启发式合并,然后每当我们合并的时候我们都要暴力拆小的一部分重复以上部分,总时 ...

  3. oracle的group by问题

    ORA-00979 不是 GROUP BY 表达式”这个错误,和我前面介绍的另外一个错误ORA-00937一样使很多初学oracle的人爱犯的. 我在介绍使用聚合函数中用group by来分组数据时特 ...

  4. ibeacon UUID

    import sys; import uuid; s=uuid.uuid4().hex #s="f6bc15e0939046679be1866ec8a199dc" sys.stdo ...

  5. CI框架浅析

    CI框架浅析(全篇)     业余花了点时间看看CodeIgniter框架(简称CI),CI目前的稳定版本是 3.X,4.0版本已经出来了,但还在测试中,所以我分析的还是 3.x 版本. CI是一个很 ...

  6. PHP 抽象类,接口,抽象方法,静态方法

    1.Abstract class(抽象类) 抽象类是指在 class 前加了 abstract 关键字且存在抽象方法(在类方法 function 关键字前加了 abstract 关键字)的类. 抽象类 ...

  7. bzoj4128 Matrix

    传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=4128 [题解] 矩阵版本的BSGS. 至于如何不需要求逆,详见:http://www.cnb ...

  8. bzoj 2748 DP

    比较裸的背包,w[i][j]代表到第I个操作的时候音量j能不能达到,然后转移就行了. /******************************************************** ...

  9. 【git】git提交忽略不必要的文件或文件夹

    对于经常使用Git的朋友来说,.gitignore配置一定不会陌生.废话不说多了,接下来就来说说这个.gitignore的使用. 首先要强调一点,这个文件的完整文件名就是".gitignor ...

  10. inno setup 5 添加快捷方式默认选中

    转载:https://www.cnblogs.com/x_wukong/p/5012412.html https://zhidao.baidu.com/question/312006120.html ...