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

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

后缀数组功能强大,但是最令人头疼的地方是模板太难背容易写错。用这个方法,只需要用上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. 【ZJ选讲·字符串折叠】

    给一个字符串(len<=100) 把这个字符串折叠(就是压缩) 记 X(子串) 表示重复 X次该子串 比如 3(orz)  orzorzorz  来点神奇例子: AAAAAAAAAA ...

  2. 【BZOJ 4514】[Sdoi2016]数字配对 费用流

    利用spfa流的性质,我直接拆两半,正解分奇偶(妙),而且判断是否整除且质数我用的是暴力根号,整洁判断质数个数差一(其他非spfa流怎么做?) #include <cstdio> #inc ...

  3. 【NOIP模拟赛】天神下凡 动态开点线段树

    这些圆一定是在同一水平面上的,由于他们没有相交,因此我们发现他们每个人与外界关系可以分为,1.存在并圈圈 2.存在圈圈并被割,因此我们把所有的圆都加1,把被割的在加1,就可以啦,因此我们开一个线段树, ...

  4. 2017年研究生数学建模D题(前景目标检测)相关论文与实验结果

    一直都想参加下数学建模,通过几个月培训学到一些好的数学思想和方法,今年终于有时间有机会有队友一起参加了研究生数模,but,为啥今年说不培训直接参加国赛,泪目~_~~,然后比赛前也基本没看,直接硬刚.比 ...

  5. [SDOI2011]消防/[NOIP2007] 树网的核

    消防 题目描述 某个国家有n个城市,这n个城市中任意两个都连通且有唯一一条路径,每条连通两个城市的道路的长度为zi(zi<=1000). 这个国家的人对火焰有超越宇宙的热情,所以这个国家最兴旺的 ...

  6. [fzu 2273]判断两个三角形的位置关系

    首先判断是否相交,就是枚举3*3对边的相交关系. 如果不相交,判断包含还是相离,就是判断点在三角形内还是三角形外.两边各判断一次. //http://acm.fzu.edu.cn/problem.ph ...

  7. php模板引擎smarty

    一. smarty的特点 速度:相对于其他模板引擎,速度较快 编译型:在下次访问模板时直接访问编译文件,不再进行模板重新编译 缓存技术:可以将用户最终看到的HTML文件缓存成一个静态HTML 插件技术 ...

  8. 51nod K 汽油补给 大根堆+小根堆....

    题目传送门 用优先队列瞎搞... 想着在每个地方 先算上一个点到这一个点要花费多少钱 这个用小根堆算就好 然后在这个地方加油 把油钱比自己多的替代掉 这个用大根堆维护一下 然后两个堆之间信息要保持互通 ...

  9. bzoj2002 弹飞绵羊 lct版

    这道题就是维护一个有根的lct 一开始建树全部建虚边 求多少次弹出就是求他到根的距离(根为n+1) 这里有个小技巧 将n+1作为根而没有虚根操作起来会比较方便 #include<cstdio&g ...

  10. 编辑器KindEditor的使用

    1.具体使用方法看点这里 2.下载点这里 3.文件夹说明 ├── asp asp示例,删掉 ├── asp.net asp.net示例,删掉 ├── attached 空文件夹,放置关联文件attac ...