[BZOJ4650][NOI2016]优秀的拆分(SAM构建SA)
关于解法这个讲的很清楚了,主要用了设关键点的巧妙思想。
主要想说的是一个刚学的方法:通过后缀自动机建立后缀树,再转成后缀数组。
后缀数组功能强大,但是最令人头疼的地方是模板太难背容易写错。用这个方法,只需要用上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)的更多相关文章
- [UOJ#219][BZOJ4650][Noi2016]优秀的拆分
[UOJ#219][BZOJ4650][Noi2016]优秀的拆分 试题描述 如果一个字符串可以被拆分为 AABBAABB 的形式,其中 A 和 B 是任意非空字符串,则我们称该字符串的这种拆分是优秀 ...
- BZOJ4650 [NOI2016]优秀的拆分 【后缀数组】
题目 如果一个字符串可以被拆分为 AABBAABB 的形式,其中 AA 和 BB 是任意非空字符串,则我们称该字符串的这种拆 分是优秀的.例如,对于字符串 aabaabaa,如果令 A=aabA=aa ...
- UOJ#219/BZOJ4650 [NOI2016]优秀的拆分 字符串 SA ST表
原文链接http://www.cnblogs.com/zhouzhendong/p/9025092.html 题目传送门 - UOJ#219 (推荐,题面清晰) 题目传送门 - BZOJ4650 题意 ...
- BZOJ4650 NOI2016优秀的拆分(后缀数组)
显然只要求出以每个位置开始的AA串数量就可以了,将其和反串同位置的结果乘一下,加起来就是答案.考虑对每种长度的字符串计数.若当前考虑的A串长度为x,我们每隔x个字符设一个关键点,求出相邻两关键点的后缀 ...
- bzoj千题计划317:bzoj4650: [Noi2016]优秀的拆分(后缀数组+差分)
https://www.lydsy.com/JudgeOnline/problem.php?id=4650 如果能够预处理出 suf[i] 以i结尾的形式为AA的子串个数 pre[i] 以i开头的形式 ...
- 题解【bzoj4650 [NOI2016]优秀的拆分】
Description 求对每一个连续字串将它切割成形如 AABB 的形式的方案数之和 Solution 显然 AABB 是由两个 AA 串拼起来的 考虑维护两个数组 a[i] 和 b[i] ,其中 ...
- BZOJ4650: [Noi2016]优秀的拆分
考场上没秒的话多拿5分并不划算的样子. 思想其实很简单嘛. 要统计答案,求以每个位置开始和结束的AA串数量就好了.那么枚举AA中A的长度L,每L个字符设一个关键点,这样AA一定经过相邻的两个关键点.计 ...
- BZOJ4650: [Noi2016]优秀的拆分(hash 调和级数)
题意 题目链接 Sol NOI的题都这么良心么.. 先交个\(n^4\)暴力 => 75 hash优化一下 => 90 然后\(90\)到\(100\)分之间至少差了\(10\)难度台阶= ...
- bzoj4650: [Noi2016]优秀的拆分 hash
好气啊,没开longlong又biubiu了 底层: 用hash或者奇奇怪怪的算法兹磁logn求最长公共前后缀 思路: 统计出从一个点开始和结束的形如AA的子串的个数 统计的时候把相邻的结果相乘加起来 ...
随机推荐
- 【BZOJ 2879】[Noi2012]美食节 费用流
思路同修车,就是多了一个骚气的操作:动态加边,我们通过spfa流的过程可以知道,我们一次只会跑一流量,最后一层边跑过就不会再悔改,所以说我们只会用到一大片里面的很少的点,所以我们如果可以动态加边的话我 ...
- [COGS 1535] [ZJOI2004]树的果实 树状数组+桶
我们用树状数组做差就可以解决一切问题,我用桶排并用此来表示出第几大就可以直接求前缀和了 #include<cstdio> #include<algorithm> #define ...
- 判断当前系统当前浏览器是否安装启用 Adobe Flash Player,检查在chrome中的状态
一.判断当前所在系统 let sUserAgent = navigator.userAgent;let isWin = (navigator.platform == "Win32" ...
- readelf用法小记
By francis_hao Feb 14,2017 显示ELF文件的信息 用法概述 readelf和objdump类似,不过,readelf会显示更详细的信息,而且独立于BFD库,因此当BFD ...
- MUI 按两次返回键退出应用 及 地理位置获取
<span style="font-size:14px;"><span style="font-size:14px;"> mui.plu ...
- 我自己的python开发环境
1.开发工具 eclipse 所有的版本下载: https://www.eclipse.org/downloads/index-packages.php , 我下载的是比较低的版本:https://w ...
- 「6月雅礼集训 2017 Day1」看无可看
[题目大意] 给出n个数,a[1]...a[n],称作集合S,求
- 51nod 1254 最大子段和 V2 ——单调栈
N个整数组成的序列a[1],a[2],a[3],…,a[n],你可以对数组中的一对元素进行交换,并且交换后求a[1]至a[n]的最大子段和,所能得到的结果是所有交换中最大的.当所给的整数均为负数时和为 ...
- 【CF1027D】Mouse Hunt(拓扑排序,环)
题意:给定n个房间,有一只老鼠可能从其中的任意一个出现, 在第i个房间设置捕鼠夹的代价是a[i],若老鼠当前在i号房间则下一秒会移动到b[i]号, 问一定能抓住老鼠的最小的总代价 n<=2e5, ...
- LCD实验学习笔记(四):系统时钟
一般CPU频率(FCLK)高于内存.网卡等设备频率(HCLK),而串口.USB.I2C等设备频率(PCLK)更低. 系统时钟: 系统时钟源为晶振,初始频率12MHz. 通过设置MPLLCON寄存器的M ...