原文链接http://www.cnblogs.com/zhouzhendong/p/9025092.html

题目传送门 - UOJ#219 (推荐,题面清晰)

题目传送门 - BZOJ4650

题意

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

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

  多组数据,数据组数$\leq 10,n\leq 30000$

题解

  考虑先处理$AA$类型的字符串,对于每一个位置$i$,分别处理以$i$位置开头或结尾的$AA$类型字符串个数。

  如果完成了这个处理,那么最后的统计便毫不费力。(注意开longlong)

  考虑如何处理。

  首先,枚举$A$串的长度,记为$L$。我们设置一些关键点,依次为$1L,2L,3L,\cdots,iL,(i+1)L,\cdots$。

  考虑处理相邻两个关键点对答案的贡献。对于第$i$对相邻的关键点,我们需要得到一个特殊的最长公共子串。

  这个最长公共子串由结尾为$iL,(i+1)L$的长度不大于$L$的最长公共后缀(LCS) 和 开头为$iL,(i+1)L$的长度不大于$L$的最长公共前缀(LCP) 拼接而成。

  于是你可以通过删除一些这个最长公共子串的后缀和前缀,使得包含两个关键点的相同串首尾相接,来得到长度为$2L$的$AA$类型字符串。

  这样的串对于一开始要处理的那个东西有一个区间的贡献。可以用差分来搞定。

  至于求$LCS$和$LCP$,我们只需要先后缀数组+ST表预处理一下,就可以了。

  时间复杂度$O(n\log n)$。

代码

#include <bits/stdc++.h>
#define rank r_a_n_k
using namespace std;
typedef long long LL;
const int N=60005;
int T,n,m,tmp[N],SA[N],rank[N],tax[N],height[N];
int ST[N][18],A[N],B[N];
char s[N];
void Sort(int n){
for (int i=0;i<=m;i++)
tax[i]=0;
for (int i=1;i<=n;i++)
tax[rank[i]]++;
for (int i=1;i<=m;i++)
tax[i]+=tax[i-1];
for (int i=n;i>=1;i--)
SA[tax[rank[tmp[i]]]--]=tmp[i];
}
bool cmp(int rk[],int x,int y,int w){
return rk[x]==rk[y]&&rk[x+w]==rk[y+w];
}
void Suffix_Array(char s[],int n){
memset(SA,0,sizeof SA);
memset(tmp,0,sizeof tmp);
memset(rank,0,sizeof rank);
memset(height,0,sizeof height);
for (int i=1;i<=n;i++)
rank[i]=s[i],tmp[i]=i;
m=127;
Sort(n);
for (int w=1,p=0;p<n;w<<=1,m=p){
p=0;
for (int i=n-w+1;i<=n;i++)
tmp[++p]=i;
for (int i=1;i<=n;i++)
if (SA[i]>w)
tmp[++p]=SA[i]-w;
Sort(n);
swap(tmp,rank);
rank[SA[1]]=p=1;
for (int i=2;i<=n;i++)
rank[SA[i]]=cmp(tmp,SA[i],SA[i-1],w)?p:++p;
}
for (int i=1,j,k=0;i<=n;height[rank[i++]]=k)
for (k=max(k-1,0),j=SA[rank[i]-1];s[i+k]==s[j+k];k++);
}
void GetST(int n){
memset(ST,0,sizeof ST);
for (int i=1;i<=n;i++){
ST[i][0]=height[i];
for (int j=1;j<18;j++){
ST[i][j]=ST[i][j-1];
if (i-(1<<(j-1))>0)
ST[i][j]=min(ST[i][j],ST[i-(1<<(j-1))][j-1]);
}
}
}
int QueryST(int L,int R){
int val=floor(log(R-L+1)/log(2));
return min(ST[L+(1<<val)-1][val],ST[R][val]);
}
int LCP(int i,int j){
i=rank[i],j=rank[j];
return QueryST(min(i,j)+1,max(i,j));
}
int LCS(int i,int j){
return LCP(n*2-i+2,n*2-j+2);
}
int main(){
scanf("%d",&T);
while (T--){
memset(s,0,sizeof s);
scanf("%s",s+1);
n=strlen(s+1);
s[n+1]='#';
for (int i=n;i>=1;i--)
s[n*2-i+2]=s[i];
Suffix_Array(s,n*2+1);
GetST(n*2+1);
memset(A,0,sizeof A);
memset(B,0,sizeof B);
for (int L=1;L<=n;L++)
for (int i=1;i<=n/L-1;i++){
int lcp=min(LCP(L*i,L*(i+1)),L);
int lcs=min(LCS(L*i,L*(i+1)),L);
if (lcp+lcs<=L)
continue;
int l=i*L-lcs+1,r=(i+1)*L+lcp-1;
int cov=lcp+lcs-(L+1);
B[l]++,B[l+cov+1]--;
A[r-cov]++,A[r+1]--;
}
for (int i=1;i<=n;i++)
A[i]+=A[i-1],B[i]+=B[i-1];
LL ans=0;
for (int i=1;i<=n;i++)
ans+=1LL*A[i]*B[i+1];
printf("%lld\n",ans);
}
return 0;
}

  

  

UOJ#219/BZOJ4650 [NOI2016]优秀的拆分 字符串 SA ST表的更多相关文章

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

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

  2. BZOJ2534 Uva10829L-gap字符串 字符串 SA ST表

    原文链接https://www.cnblogs.com/zhouzhendong/p/9240665.html 题目传送门 - BZOJ2534 题意 有一种形如 $uvu$ 形式的字符串,其中 $u ...

  3. UOJ#219. 【NOI2016】优秀的拆分 [后缀数组 ST表]

    #219. [NOI2016]优秀的拆分 题意:求有多少AABB样子的子串,拆分不同的同一个子串算多个 一开始一直想直接求,并不方便 然后看了一眼Claris的题解的第一行就有思路了 如果分开,求\( ...

  4. BZOJ4650 [NOI2016]优秀的拆分 【后缀数组】

    题目 如果一个字符串可以被拆分为 AABBAABB 的形式,其中 AA 和 BB 是任意非空字符串,则我们称该字符串的这种拆 分是优秀的.例如,对于字符串 aabaabaa,如果令 A=aabA=aa ...

  5. BZOJ4556 [Tjoi2016&Heoi2016]字符串 SA ST表 二分答案 主席树

    原文链接https://www.cnblogs.com/zhouzhendong/p/BZOJ4556.html 题目传送门 - BZOJ4556 题意 给定一个长度为 $n$ 的字符串 $s$ . ...

  6. BZOJ3172 [Tjoi2013]单词 字符串 SA ST表

    原文链接http://www.cnblogs.com/zhouzhendong/p/9026543.html 题目传送门 - BZOJ3172 题意 输入$n(n\leq 200)$个字符串,保证长度 ...

  7. BZOJ4650 NOI2016优秀的拆分(后缀数组)

    显然只要求出以每个位置开始的AA串数量就可以了,将其和反串同位置的结果乘一下,加起来就是答案.考虑对每种长度的字符串计数.若当前考虑的A串长度为x,我们每隔x个字符设一个关键点,求出相邻两关键点的后缀 ...

  8. BZOJ4650: [Noi2016]优秀的拆分

    考场上没秒的话多拿5分并不划算的样子. 思想其实很简单嘛. 要统计答案,求以每个位置开始和结束的AA串数量就好了.那么枚举AA中A的长度L,每L个字符设一个关键点,这样AA一定经过相邻的两个关键点.计 ...

  9. BZOJ4650: [Noi2016]优秀的拆分(hash 调和级数)

    题意 题目链接 Sol NOI的题都这么良心么.. 先交个\(n^4\)暴力 => 75 hash优化一下 => 90 然后\(90\)到\(100\)分之间至少差了\(10\)难度台阶= ...

随机推荐

  1. [转]phpstorm激活码注册码序列号

    浏览器打开 http://idea.lanyus.com/ , 点击页面中的“获得注册码”,然后在注册时切换至Activation Code选项,输入获得的注册码一长串字符串,就可以注册成功!(推荐方 ...

  2. Python traceback的优雅处理

    刚接触Python的时候,简单的异常处理已经可以帮助我们解决大多数问题,但是随着逐渐地深入,我们会发现有很多情况下简单的异常处理已经无法解决问题了,如下代码,单纯的打印异常所能提供的信息会非常有限. ...

  3. C#的winform中控制TextBox中只能输入数字

    C#的winform中控制TextBox中只能输入数字 private void textBox3_KeyPress(object sender, System.Windows.Forms.KeyPr ...

  4. GitHub访问慢

    问题描述 一直都觉得访问 GitHub 时速度非常慢,刷新一下都要等好久!于是尝试一下能否解决掉... github.com assets-cdn.github.com avatar2.githubu ...

  5. Confluence 6 home 目录中的内容

    Confluence home 目录存储了 Confluence 在运行中所使用的数据.下面对 Confluence home 目录中使用的数据和文件进行一些说明: confluence.cfg.xm ...

  6. Confluence 6 SQL Server 创建一个数据库和数据库用户

    一旦你成功安装了 SQL Server 服务器,请按照下面的方法为你的 Confluence 创建数据库用户和数据库: 使用你的 SQL 管理员权限,创建一个新的数据库(例如 confluence). ...

  7. Confluence 6 编辑和删除用户宏

    编辑一个用户宏 希望对一个用户宏进行编辑: 进入  > 基本配置(General Configuration) > 用户宏(User Macros) 在相关的宏的边上,单击 编辑(Edit ...

  8. gnuradio 初次使用

    参考链接: 入门 http://www.cnblogs.com/moon1992/p/5739027.html 创建模块 http://www.cnblogs.com/moon1992/p/54246 ...

  9. Linux 用户切换、修改用户名、修改密码

    一.用户切换 "$":普通用户提示符 "#":root用户提示符 1.普通用户到root: 方式一:命令:su然后输入root密码 此种方式只是切换了root身 ...

  10. (批量更新)对多个符合条件的id做更新操作

    需求描述:把checkbox勾选的对应id的记录的标志位置1或0,这个其实不难的,不过我自己做的话,肯定是多次访问数据库做更新,看了老大的代码,发现差距不是一般的大,老大把sql灵活运用,结果一次访问 ...