【问题描述】

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

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

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

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

以下事项需要注意:

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

2. 在一个拆分中,允许出现 A = B。例如 cccc 存在拆分 A = B = c。

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

【输入格式】

每个输入文件包含多组数据。输入文件的第一行只有一个整数 T,表示数据 的组数。保证 1 ≤ T ≤ 10。

接下来 T 行,每行包含一个仅由英文小写字母构成的字符串 S,意义如题 所述。

【输出格式】

输出 T 行,每行包含一个整数,表示字符串 S 所有子串的所有拆分中, 总共有多少个是优秀的拆分。

【样例输入】

4
aabbbb
cccccc
aabaabaabaa
bbaabaababaaba

【样例输出】

3 5 4 7

【样例说明】

我们用 S[i, j] 表示字符串 S 第 i 个字符到第 j 个字符的子串(从 1 开 始计数)。

第一组数据中,共有 3 个子串存在优秀的拆分:

S[1,4] = aabb,优秀的拆分为 A = a, B = b;

S[3,6] = bbbb,优秀的拆分为 A = b, B = b;

S[1,6] = aabbbb,优秀的拆分为 A = a, B = bb。

而剩下的子串不存在优秀的拆分,所以第一组数据的答案是 3。

第二组数据中, 有两类,总共 4 个子串存在优秀的拆分:

对于子串 S[1,4] = S[2,5] = S[3,6] = cccc, 它们优秀的拆分相同,均为 A = c, B = c,但由于这些子串位置不同,因此要计算 3 次;

对于子串 S[1,6] = cccccc,它优秀的拆分有 2 种: A = c, B = cc 和 A = cc, B = c,它们是相同子串的不同拆分,也都要计入答案。

所以第二组数据的答案是 3 + 2 = 5。

第三组数据中, S[1,8] 和 S[4,11] 各有 2 种优秀的拆分,其中 S[1,8] 是 问题描述中的例子,所以答案是 2 + 2 = 4。

第四组数据中, S[1,4], S[6,11], S[7,12], S[2,11], S[1,8] 各有 1 种优秀 的拆分, S[3,14] 有 2 种优秀的拆分,所以答案是 5 + 2 = 7。

【更多样例】

下载

【样例 2 输入输出】

见目录下的 excellent/excellent2.in 与 excellent/excellent2.ans。

【样例 3 输入输出】

见目录下的 excellent/excellent3.in 与 excellent/excellent3.ans。

【子任务】

对于全部的测试点,保证 1 ≤ T ≤ 10。 以下对数据的限制均是对于单组输入数据而言的,也就是说同一个测试点下的 T 组数据均满足限制条件。

我们假定 n 为字符串 S 的长度,每个测试点的详细数据范围见下表:

【来源】

NOI2016 Day1 T1

  这道题有点难想到正解。

  枚举长度i,然后把字符串拆分成许多连续的长度为i的子串,通过比较LCS与LCP得出一段的答案,这里发现答案是区间加法,考虑用线段树很可能超时,这里用的是差分,看程序很好理解。还有一个地方要注意:更新答案时可能会重复计算,只需要确保每次枚举都只在一段限定的区间更新,就不会出现重叠。

 #include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;
const int N=;
struct SAM{
char s[N];
int fa[N],pos[N],sa[N],rank[N];
int son[N][],end[N],rht[N],lcp[N];
int ch[N][],len[N],id[N],tot;
int od[N],wv[N],lst,cnt;
int mm[N],Min[N][];
void Init(){
memset(s,,sizeof(s));
memset(ch,,sizeof(ch));
memset(end,,sizeof(end));
memset(son,,sizeof(son));
memset(pos,,sizeof(pos));
lst=cnt=;tot=;
} void Insert(int c){
int p=lst,np=lst=++cnt;end[lst]=;
id[len[np]=len[p]+]=np;rht[np]=;
while(p&&!ch[p][c])ch[p][c]=np,p=fa[p];
if(!p)fa[np]=;
else{
int q=ch[p][c],nq;
if(len[q]==len[p]+)fa[np]=q;
else{
len[nq=++cnt]=len[p]+;
fa[nq]=fa[q];fa[q]=fa[np]=nq;
memcpy(ch[nq],ch[q],sizeof(ch[q]));
while(ch[p][c]==q)ch[p][c]=nq,p=fa[p];
}
}
} void Get_Right(){
for(int i=;i<=cnt;i++)wv[len[i]]++;
for(int i=;i<=cnt;i++)wv[i]+=wv[i-];
for(int i=;i<=cnt;i++)od[wv[len[i]]--]=i;
for(int i=cnt;i>=;i--)rht[fa[od[i]]]+=rht[od[i]];
} void Build_Tree(){
int l=strlen(s+);
for(int i=l;i>=;i--)Insert(s[i]-'a');
for(int i=l;i>=;i--)
for(int x=id[i],p=l+;x&&!pos[x];x=fa[x])
p-=len[x]-len[fa[x]],pos[x]=p;
for(int x=;x<=cnt;x++)son[fa[x]][s[pos[x]]-'a']=x;
} void DFS(int x,int l){
if(end[x])sa[rank[l-len[x]+]=++tot]=l-len[x]+;
for(int i=;i<;i++)if(son[x][i])DFS(son[x][i],l);
} void Build_SA(){
int l=strlen(s+),k=;DFS(,l);
for(int i=,j;i<=l;lcp[rank[i++]]=k)
for(k?k--:k,j=sa[rank[i]-];s[i+k]==s[j+k];k++);
mm[]=-;
for(int i=;i<=l;i++){
mm[i]=(i&(i-))?mm[i-]:mm[i-]+;
Min[i][]=lcp[i];
}
for(int k=;k<=mm[l];k++)
for(int i=;i+(<<k-)<=l;i++)
Min[i][k]=min(Min[i][k-],Min[i+(<<(k-))][k-]);
} int LCP(int x,int y){
if(x>y)swap(x,y);x+=;int k=mm[y-x+];
int ret=min(Min[x][k],Min[y-(<<k)+][k]);
return ret;
}
}A,B; int ln,T,f[N],g[N];char s[N];
int Get_LCP(int x,int y){return A.LCP(A.rank[x],A.rank[y]);}
int Get_LCS(int x,int y){return B.LCP(B.rank[ln-x+],B.rank[ln-y+]);} int main(){
freopen("excellent.in","r",stdin);
freopen("excellent.out","w",stdout);
scanf("%d",&T);
while(T--){
A.Init();B.Init();
scanf("%s",s+);ln=strlen(s+);
for(int i=;i<=ln;i++){A.s[i]=s[i];B.s[i]=s[ln-i+];}
A.Build_Tree();A.Build_SA();
B.Build_Tree();B.Build_SA();
for(int i=;i<=ln;i++)f[i]=g[i]=; for(int i=,l,r,x;i+i<=ln;i++)
for(int j=i;(x=j+i)<=ln;j+=i)if(s[j]==s[x]){
l=x-Get_LCS(j,x)+;r=x+Get_LCP(j,x)-;
l=max(l+i-,x);r=min(r,x+i-);
if(l>r)continue;
f[l]++,f[r+]--;
g[l-i-i+]++,g[r+-i-i+]--;
}
long long ans=;
for(int i=;i<=ln;i++)f[i]+=f[i-],g[i]+=g[i-];
for(int i=;i<ln;i++)ans+=f[i]*g[i+];
printf("%lld\n",ans);
}
return ;
}

  

字符串(后缀自动机):NOI 2016 优秀的拆分的更多相关文章

  1. [LOJ 2083][UOJ 219][BZOJ 4650][NOI 2016]优秀的拆分

    [LOJ 2083][UOJ 219][BZOJ 4650][NOI 2016]优秀的拆分 题意 给定一个字符串 \(S\), 求有多少种将 \(S\) 的子串拆分为形如 AABB 的拆分方案 \(| ...

  2. NOI 2016 优秀的拆分 (后缀数组+差分)

    题目大意:给你一个字符串,求所有子串的所有优秀拆分总和,优秀的拆分被定义为一个字符串可以被拆分成4个子串,形如$AABB$,其中$AA$相同,$BB$相同,$AB$也可以相同 作为一道国赛题,95分竟 ...

  3. [NOI 2016]优秀的拆分

    Description 题库链接 给你一个长度为 \(n\) 的只含小写字母的字符串 \(S\) ,计算其子串有多少优秀的拆分. 如果一个字符串能被表示成 \(AABB\) 的形式,其中 \(A,B\ ...

  4. [bzoj 4650][NOI 2016]优秀的拆分

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

  5. 解题:NOI 2016 优秀的拆分

    题面 其实题目不算很难,但是我调试的时候被玄学了,for循环里不写空格会RE,写了才能过.神**调了一个多小时是这么个不知道是什么的玩意(真事,可以问i207M=.=),心态爆炸 发现我们只要找AA或 ...

  6. 模板—字符串—后缀自动机(后缀自动机+线段树合并求right集合)

    模板—字符串—后缀自动机(后缀自动机+线段树合并求right集合) Code: #include <bits/stdc++.h> using namespace std; #define ...

  7. [TJOI2019]甲苯先生和大中锋的字符串——后缀自动机+差分

    题目链接: [TJOI2019]甲苯先生和大中锋的字符串 对原串建后缀自动机并维护$parent$树上每个点的子树大小,显然子树大小为$k$的节点所代表的子串出现过$k$次,那么我们需要将$[len[ ...

  8. Wannafly Camp 2020 Day 2D 卡拉巴什的字符串 - 后缀自动机

    动态维护任意两个后缀的lcp集合的mex,支持在串末尾追加字符. Solution 考虑在 SAM 上求两个后缀的 LCP 的过程,无非就是找它们在 fail 树上的 LCA,那么 LCP 长度就是这 ...

  9. bzoj3756pty的字符串(后缀自动机+计数)

    题目描述 题解 我们可以先对trie树建出广义SAM,然后维护一下right集合大小(注意right集合在广义SAM上的维护方式). 然后把匹配穿往广义SAM上匹配,假设现在匹配到了x节点,那么x的所 ...

随机推荐

  1. oracle添加数据时主键自动增长

    CREATE TABLE STUDENT( --创建学生表  ID NUMBER(10) PRIMARY KEY,   --主键ID  SNAME VARCHAR2(20), ); 此时给学生表添加数 ...

  2. JavaScript 数据类型转换(显式与隐式)

    一.数据类型 JS中有5中简单数据类型(也称为基本数据类型):Undefined.Null.Boolean.Number.String.还有一种复杂数据类型------Object,Object本质是 ...

  3. 10.7 noip模拟试题

    楼[问题背景]zhx 为他的妹子造了一幢摩天楼.[问题描述]zhx 有一幢摩天楼. 摩天楼上面有 M 个观光电梯,每个观光电梯被两个整数

  4. tj

    --统计set @collSql='select sum(case Ca_IssueType when 0 then 1 else 0 end) as IssueCount,sum(case when ...

  5. C# 内存管理优化实践

    内存优化畅想系列文章已经结束了,很多读者读完之后可能觉得“然并卵”,毕竟都是给微软提的建议而已,现在都没有实现.那么为了优化内存,有没有什么我们现在就能用的技巧呢?我的答案是:有.网上关于.net内存 ...

  6. maven提示错误的解决办法

    import或者new一个的maven project的时候,提示如下错误 Description    Resource    Path    Location    TypeCannot read ...

  7. Adb工具常用操作-转(二)

    一. PC与模拟器或真机交换文件(adb pull和adb push) 在开发阶段或其他原因,经常需要将PC上的文件复制到模拟器或真机上,或将模拟机和真机上的文件复制到PC上.使用adb pull和a ...

  8. jQuery实现的向下推送图文信息滚动效果

    HTML 我们以新浪微博信息滚动为背景,html中包含了多条微博图文信息,结构如下: <div id="con"> <ul> <li> < ...

  9. 拓扑排序-DFS

    拓扑排序的DFS算法 输入:一个有向图 输出:顶点的拓扑序列 具体流程: (1) 调用DFS算法计算每一个顶点v的遍历完成时间f[v] (2) 当一个顶点完成遍历时,将该顶点放到一个链表的最前面 (3 ...

  10. python【第八篇】socket网络编程

    内容大纲 1.socke基础 两个程序通过一个双向的通信连接实现数据的交换,这个连接的一端称为一个socket. 建 立网络通信连接至少要一对端口号(socket).socket本质是编程接口(API ...