BZOJ4650 : [Noi2016]优秀的拆分
2016-08-03 01:21
531 查看
设$f_i$表示以$i$结尾的square个数,$g_i$表示以$i$开头的square个数,则$ans=\sum_{i=1}^{n-1} f_ig_{i+1}$。
枚举square中长度的一半$L$,每$L$步取一个关键点,那么每个该长度的square的肯定恰好经过两个相邻的关键点,且位置差距为$L$的字符一一匹配。所以相邻关键点之间通过后缀数组求出最长公共前后缀,即可知道存在多少该长度的square。
这相当于$f$和$g$的一段区间$+1$,差分前缀和后单点修改即可。
时间复杂度$O(n\log n)$。
枚举square中长度的一半$L$,每$L$步取一个关键点,那么每个该长度的square的肯定恰好经过两个相邻的关键点,且位置差距为$L$的字符一一匹配。所以相邻关键点之间通过后缀数组求出最长公共前后缀,即可知道存在多少该长度的square。
这相当于$f$和$g$的一段区间$+1$,差分前缀和后单点修改即可。
时间复杂度$O(n\log n)$。
#include<cstdio> #include<algorithm> #include<cstring> #define N 60010 using namespace std; char s ;int T,n,i,j,Log ,l,r,f ,g ;long long ans; struct DS{ char s ; int rk ,sa ,height ,tmp ,cnt ,f[15] ; void suffixarray(int n,int m){ int i,j,k;n++; for(i=0;i<n*2+5;i++)rk[i]=sa[i]=height[i]=tmp[i]=0; for(i=0;i<m;i++)cnt[i]=0; for(i=0;i<n;i++)cnt[rk[i]=s[i]]++; for(i=1;i<m;i++)cnt[i]+=cnt[i-1]; for(i=0;i<n;i++)sa[--cnt[rk[i]]]=i; for(k=1;k<=n;k<<=1){ for(i=0;i<n;i++){ j=sa[i]-k; if(j<0)j+=n; tmp[cnt[rk[j]]++]=j; } sa[tmp[cnt[0]=0]]=j=0; for(i=1;i<n;i++){ if(rk[tmp[i]]!=rk[tmp[i-1]]||rk[tmp[i]+k]!=rk[tmp[i-1]+k])cnt[++j]=i; sa[tmp[i]]=j; } memcpy(rk,sa,n*sizeof(int)); memcpy(sa,tmp,n*sizeof(int)); if(j>=n-1)break; } for(j=rk[height[i=k=0]=0];i<n-1;i++,k++) while(~k&&s[i]!=s[sa[j-1]+k])height[j]=k--,j=rk[sa[j]+1]; } void build(){ int i,j; for(i=1;i<=n;i++)f[0][i]=height[i]; for(j=1;j<15;j++)for(i=1;i+(1<<j-1)<=n;i++)f[j][i]=min(f[j-1][i],f[j-1][i+(1<<j-1)]); } inline int ask(int x,int y){ int k=Log[y-x+1]; return min(f[k][x],f[k][y-(1<<k)+1]); } inline int lcp(int x,int y){ x=rk[x],y=rk[y]; if(x>y)swap(x,y); return ask(x+1,y); } }A,B; inline int lcp(int x,int y){return A.lcp(x-1,y-1);} inline int lcs(int x,int y){return B.lcp(n-x,n-y);} int main(){ for(i=2;i<N;i++)Log[i]=Log[i>>1]+1; scanf("%d",&T); while(T--){ scanf("%s",s); n=strlen(s); for(i=0;i<n*2+5;i++)A.s[i]=B.s[i]=0; for(i=0;i<n;i++)A.s[i]=B.s[n-i-1]=s[i]; A.suffixarray(n,128); A.build(); B.suffixarray(n,128); B.build(); for(i=1;i<=n;i++)f[i]=g[i]=0; for(i=1;i+i<=n;i++)for(j=i+i;j<=n;j+=i)if(s[j-1]==s[j-i-1]){ l=j-lcs(j,j-i)+1,r=j+lcp(j,j-i)-1; l=max(l+i-1,j),r=min(r,j+i-1); if(l<=r){ f[l]++,f[r+1]--; g[l-i-i+1]++,g[r+1-i-i+1]--; } } for(i=1;i<=n;i++)f[i]+=f[i-1],g[i]+=g[i-1]; for(ans=0,i=1;i<n;i++)ans+=f[i]*g[i+1]; printf("%lld\n",ans); } return 0; }
相关文章推荐
- 最短路径算法dijkstra
- Ember.js如何与后端服务交互?adapter、store、ember data关系揭秘
- OpenCV学习笔记(三):邻域操作
- HDU 5787 K-wolf Number 数位dp
- 哈夫曼树的建立以及哈夫曼编码
- Mac搭建Apache+PHP+MySQL环境
- 邻接矩阵实现prim算法
- Angular Service和Fatory的差异
- Android官方文档之Location and Sensors APIs(上)
- JHTP自测题_第十三章_图形及Java2D
- Android中TextView不能识别电信号码11888的问题。
- 自定义Dom4j类库
- 理解抽象类和接口的区别 ---- 是不是 和 有没有 --- 的区别
- 解决eclipse配置Tomcat时找不到server选项
- 邻接矩阵实现克鲁斯卡尔算法
- POJ 2387 Til the Cows Come Home
- springMVC------Rest风格,HiddenHttpMethodFilter的作用
- 关联Tomcat服务器
- mysql的基本命令
- 在二叉树中找出和为某一值的所有路径