如果一个字符串可以被拆分为\(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. 数据表管理admin

    知识预览 admin组件使用 admin源码解析 回到顶部 admin组件使用 Django 提供了基于 web 的管理工具. Django 自动管理工具是 django.contrib 的一部分.你 ...

  2. 史上最全docker基础知识汇总

    正文 Docker常用命令 run docker run [OPTIONS] IMAGE [COMMAND] [ARG...] -e设置环境变量:-e username=zhj --name为容器指定 ...

  3. 【NOIP2009】道路游戏

    Description 小新正在玩一个简单的电脑游戏. 游戏中有一条环形马路,马路上有 nn 个机器人工厂,两个相邻机器人工厂之间由一小段马路连接.小新以某个机器人工厂为起点,按顺时针顺序依次将这 n ...

  4. UVA - 12099 The Bookcase

    No wonder the old bookcase caved under the massive piles of books Tom had stacked on it. He had bett ...

  5. 利用pyecharts将数据可视化

    可视化展示在数据分析领域中是一个至关重要的点,好的可视化展示对我们的结果分析有更好的支持作用. 一.问题 在数据分析的时代里面我们需要将数据的可视化展现出来,更加方便用户的观察.如下图 有些时候我们需 ...

  6. 程序员IT狗有什么副业可以做呢?

    1. 开篇 副业有很多,全网有做什么公众号.闲鱼.手机卡,各种各样的都有,大部分是骗子,小部分是通过自己的努力,获得了成功. 从年初就开始实践如何做一个自由职业者,近大半年有一些感受正好一起分享交流一 ...

  7. 马蜂窝 IM 移动端架构的从 0 到 1

    (马蜂窝技术原创内容,公众号 ID:mfwtech) 移动互联网技术改变了旅游的世界,这个领域过去沉重的信息分销成本被大大降低.用户与服务供应商之间.用户与用户之间的沟通路径逐渐打通,沟通的场景也在不 ...

  8. Java学习笔记之封装与继承

    封装 1,将属性定义为私有的(private)   不能通过  对象名.属性  来直接访问,但是可以通过方法来间接的访问, 2,封装的意义:公有属性可以被随意修改,并且不能被程序察觉.封装后,别人通过 ...

  9. Codeforces 986B - Petr and Permutations

    Description\text{Description}Description Given an array a[], swap random 2 number of them for 3n or  ...

  10. 移动端前端常见的触摸相关事件touch、tap、swipe

    前端的很多事件在PC端和浏览器端可公用,但有些事件却只在移动端产生,如触摸相关的事件 本文整理了移动端常见的一些事件,包括原生支持的click.touch.tap.swipe事件,也有定义型的gest ...