如果一个字符串可以被拆分为\(AABB\)的形式,其中$A和 B是任意非空字符串,则我们称该字符串的这种拆分是优秀的。

例如,对于字符串\(aabaabaa\),如果令\(A=aab\),\(B=a\),我们就找到了这个字符串拆分成\(AABB\)的一种方式。

一个字符串可能没有优秀的拆分,也可能存在不止一种优秀的拆分。比如我们令\(A=a\),\(B=baa\),也可以用 AABB表示出上述字符串;但是,字符串\(abaabaa\)就没有优秀的拆分。

现在给出一个长度为\(n\)的字符串\(S\),我们需要求出,在它所有子串的所有拆分方式中,优秀拆分的总个数。这里的子串是指字符串中连续的一段。

以下事项需要注意:

  • 出现在不同位置的相同子串,我们认为是不同的子串,它们的优秀拆分均会被记入答案。

  • 在一个拆分中,允许出现\(A=B\)。例如\(cccc\)存在拆分\(A=B=c\)。

  • 字符串本身也是它的一个子串。

对于\(AABB\),我们完全可以只考虑\(AA\),这样令\(f[i]\)表示以i结尾的\(AA\)数量,\(g[i]\)表示以\(i\)开头的\(AA\)数量,那么显然就是\(sigma(f[i]g[i+1])\)。

对于\(AA\)怎么求,大体的思路和URAL1297:Palindrome求回文串是一样的,就是通过比较后缀的公共前缀来得到AA的长度,进而求出这段区间内\(f[i]g[i]\)的值。

但是这样显然是\(O(n^{2})\)的。

我们用分块的思想,枚举\(l\),将字符串分成\(l\)大小的块,则长度为\(l\)的\(AA\)一定最少跨过两个块,于是对于块边界点,求一次公共前缀和后缀,拼在一起就是我们所要的答案,复杂度调和级数\(O(n×log_{2}^{n})\)。

注意,为了让复杂度正确,我们对区间的\(f\)和\(g\)差分。

代码:

#include<cstdio>
#include<cmath>
#include<iostream>
#include<vector>
#include<cstring>
#include<algorithm>
#include<cctype>
using namespace std;
typedef long long ll;
const int N=2e6+10;
char s[N];
int n,m,rk[N],height[N],sa[N],w[N],cas,dp[N][21],lg[N];
int f[N],g[N];
inline int qpow(int a)
{
return 1<<a;
}
inline bool pan(int *x,int i,int j,int k)
{
int ti=i+k<n?x[i+k]:-1;
int tj=j+k<n?x[j+k]:-1;
return ti==tj&&x[i]==x[j];
}
void SA_init()
{
int *x=rk,*y=height,r=256;
for(int i=0; i<r; i++)w[i]=0;
for(int i=0; i<n; i++)w[s[i]]++;
for(int i=1; i<r; i++)w[i]+=w[i-1];
for(int i=n-1; i>=0; i--)sa[--w[s[i]]]=i;
r=1;
x[sa[0]]=0;
for(int i=1; i<n; i++)
x[sa[i]]=s[sa[i]]==s[sa[i-1]]?r-1:r++;
for(int k=1; r<n; k<<=1)
{
int yn=0;
for(int i=n-k; i<n; i++)y[yn++]=i;
for(int i=0; i<n; i++)
if(sa[i]>=k)y[yn++]=sa[i]-k;
for(int i=0; i<r; i++)w[i]=0;
for(int i=0; i<n; i++)w[x[y[i]]]++;
for(int i=1; i<r; i++)w[i]+=w[i-1];
for(int i=n-1; i>=0; i--)sa[--w[x[y[i]]]]=y[i];
swap(x,y);
r=1;
x[sa[0]]=0;
for(int i=1; i<n; i++)
x[sa[i]]=pan(y,sa[i],sa[i-1],k)?r-1:r++;
}
}
inline void height_init()
{
int i,j,k=0;
for(int i=1; i<=n; i++)rk[sa[i]]=i;
for(int i=0; i<n; i++)
{
if(k)k--;
j=sa[rk[i]-1];
while(s[i+k]==s[j+k])k++;
height[rk[i]]=k;
}
}
void st_init()
{
for(int i=1; i<=n; i++)
{
dp[i-1][0]=height[i];
lg[i]=lg[i-1];
if((1<<lg[i]+1)==i)lg[i]++;
}
for(int j=1; j<=lg[n]; j++)
{
for(int i=0; i<n; i++)
{
if(i+qpow(j)-1>=n)break;
dp[i][j]=min(dp[i][j-1],dp[i+qpow(j-1)][j-1]);
}
}
}
int lcp(int a,int b)
{
int l=rk[a],r=rk[b];
if(r<l)swap(l,r);
l--;
r--;
if(r<0)return 0;
l++;
int len=r-l+1;
int k=lg[len];
int h=qpow(k);
return min(dp[l][k],dp[r-h+1][k]);
}
int main()
{
scanf("%d",&cas);
while(cas--)
{
memset(f,0,sizeof(f));
memset(g,0,sizeof(g));
cin>>s;
m=strlen(s),n=2*m+1;
s[m]='$';
for(int i=m+1; i<n; i++)
{
s[i]=s[n-i-1];
}
s[n++]=0;
SA_init();
n--;
height_init();
st_init();
for(int l=1; l<=m/2; l++)
{
for(int i=0,j=l; j<m; i+=l,j+=l)
{
int p=min(l,lcp(i,j));
int s=min(l,lcp(n-i-1,n-j-1));
if(p+s-1>=l)
{
f[j-s+l]++;
f[j+p]--;
g[i-s+1]++;
g[i+p-l+1]--;
}
}
}
ll ans=0;
for(int i=1; i<m; i++)
{
f[i]+=f[i-1];
g[i]+=g[i-1];
}
for(int i=0; i<m-1; i++)
{
ans+=(ll)f[i]*g[i+1];
}
printf("%lld\n",ans);
}
return 0;
}

『题解』[NOI2016]优秀的拆分的更多相关文章

  1. 题解-NOI2016 优秀的拆分

    NOI2016 优秀的拆分 \(T\) 组测试数据.求字符串 \(s\) 的所有子串拆成 \(AABB\) 形式的方案总和. 数据范围:\(1\le T\le 10\),\(1\le n\le 3\c ...

  2. [NOI2016]优秀的拆分&&BZOJ2119股市的预测

    [NOI2016]优秀的拆分 https://www.lydsy.com/JudgeOnline/problem.php?id=4650 题解 如果我们能够统计出一个数组a,一个数组b,a[i]表示以 ...

  3. 【BZOJ4560】[NOI2016]优秀的拆分

    [BZOJ4560][NOI2016]优秀的拆分 题面 bzoj 洛谷 题解 考虑一个形如\(AABB\)的串是由两个形如\(AA\)的串拼起来的 那么我们设 \(f[i]\):以位置\(i\)为结尾 ...

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

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

  5. [NOI2016]优秀的拆分(SA数组)

    [NOI2016]优秀的拆分 题目描述 如果一个字符串可以被拆分为 \(AABB\) 的形式,其中 A和 B是任意非空字符串,则我们称该字符串的这种拆分是优秀的. 例如,对于字符串 \(aabaaba ...

  6. luogu1117 [NOI2016]优秀的拆分

    luogu1117 [NOI2016]优秀的拆分 https://www.luogu.org/problemnew/show/P1117 后缀数组我忘了. 此题哈希可解决95分(= =) 设\(l_i ...

  7. 『题解』洛谷P1063 能量项链

    原文地址 Problem Portal Portal1:Luogu Portal2:LibreOJ Portal3:Vijos Description 在\(Mars\)星球上,每个\(Mars\)人 ...

  8. BZOJ4650:[NOI2016]优秀的拆分——题解

    https://www.lydsy.com/JudgeOnline/problem.php?id=4650 https://www.luogu.org/problemnew/show/P1117 如果 ...

  9. [NOI2016]优秀的拆分 后缀数组

    题面:洛谷 题解: 因为对于原串的每个长度不一定等于len的拆分而言,如果合法,它将只会被对应的子串统计贡献. 所以子串这个限制相当于是没有的. 所以我们只需要对于每个位置i求出f[i]表示以i为开头 ...

随机推荐

  1. 并发编程之原子操作Atomic&Unsafe

    原子操作:不能被分割(中断)的一个或一系列操作叫原子操作. 原子操作Atomic主要有12个类,4种类型的原子更新方式,原子更新基本类型,原子更新数组,原子更新字段,原子更新引用.Atomic包中的类 ...

  2. 模拟telnet协议C语言客户端程序

    首先要了解telnet协议,一下两篇blog给了我初步的思路 https://www.cnblogs.com/liang-ling/p/5833489.html 这篇有比较基础的介绍 以及IAC命令含 ...

  3. .bash_profile does not exist

    localhost:test jerry$ open .bash_profile The file /Users/je/Desktop/test/.bash_profile does not exis ...

  4. ZGC gc策略及回收过程-源码分析

    源码文件:/src/hotspot/share/gc/z/zDirector.cpp 一.回收策略 main入口函数: void ZDirector::run_service() { // Main ...

  5. VMware15.5版本下安装CentOS_7_64bit

    本文介绍在VMware15.5版本下安装CentOS7. 工具准备: 1.VMware15.5版本 2.CentOS 7 64bit ISO镜像文件 一.创建虚拟机 这部分请参照我的另一个博客“一.新 ...

  6. 渗透-简单制作过waf的中国菜刀

    0x01 简单分析 web渗透中很常见的情况,用菜刀连接免杀的一句话木马连不上,有waf 除了变形一句话木马为免杀一句话,我们还需要来制作过waf的菜刀进行连接. 这里用的一句话为 来看看菜刀连接一句 ...

  7. JavaScript专题之事件循环

    准备知识 1. 进程(process) 进程是系统资源分配一个独立单位,一个程序至少有一个进程.比方说:一个工厂代表一个 CPU, 一个车间就是一个进程,任一时刻,只能有一个进程在运行,其他进程处于非 ...

  8. Python开发【第一篇】:目录

    本系列博文包含Python基础.前端开发.Web框架.缓存以及队列等,希望可以给正在学习Python编程的朋友们提供一点帮助! .Python开发[第一篇]:目录 .Python开发[第二篇]:初始P ...

  9. ThinkPHP<6.0 SQL注入代码审计分析

    版本过多只分析大版本和使用人数较多的版本目前使用人数最多的3.2.3.审计时也是发现多个版本未公开漏洞 测试环境:  Mysql5.6/PHP5.5 首先明确的是在不使用PDO做参数绑定时ThinkP ...

  10. LeetCode刷题总结-递归篇

    递归是算法学习中很基本也很常用的一种方法,但是对于初学者来说比较难以理解(PS:难点在于不断调用自身,产生多个返回值,理不清其返回值的具体顺序,以及最终的返回值到底是哪一个?).因此,本文将选择Lee ...