$ \color{#0066ff}{ 题目描述 }$

如果一个字符串可以被拆分为\(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. 字符串本身也是它的一个子串。

\(\color{#0066ff}{输入格式}\)

每个输入文件包含多组数据。

输入的第一行只有一个整数\(T\),表示数据的组数。保证 \(1≤T≤10\)。

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

\(\color{#0066ff}{输出格式}\)

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

\(\color{#0066ff}{输入样例}\)

4
aabbbb
cccccc
aabaabaabaa
bbaabaababaaba

\(\color{#0066ff}{输出样例}\)

3
5
4
7

\(\color{#0066ff}{数据范围与提示}\)

我们用\(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\)。

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

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

\(\color{#0066ff}{题解}\)

AA和BB本质上是一样的,都是两个相同的串连接,我们考虑枚举AA和BB中间的那个分界线,那么,以那个分界线为末尾的AA和以那个分界线为开头的BB的方案乘一下就是当前位置对答案的贡献

我们可以发现, A和B的长度都不会超过\(n/2\)

我们考虑分段来维护这个东西

枚举段长从1到n的一半,枚举\(1,len+1,2*len+1,3*len+1\dots \frac{n}{len}*len + 1\)这些分界线

统计两个分界线之间的贡献

把字符串正反跑一遍SA,然后RMQ快速求出这两个分界线的向前LCPx和向后LCPy

如果\(x + y> =len + 1\)那么就说明这是一个合法的拆分,中间重合的部分,就是l和r变化的范围

在l和r的范围上差分一下就行了

// luogu-judger-enable-o2
#include<bits/stdc++.h>
#define LL long long
LL in() {
char ch; LL x = 0, f = 1;
while(!isdigit(ch = getchar()))(ch == '-') && (f = -f);
for(x = ch ^ 48; isdigit(ch = getchar()); x = (x << 1) + (x << 3) + (ch ^ 48));
return x * f;
}
const int maxn = 5e4 + 10;
int lg[maxn];
struct SA {
char s[maxn];
int rk[maxn], sa[maxn], c[maxn], x[maxn], y[maxn], h[maxn], n, m, st[maxn][20];
void init() {
memset(x, 0, sizeof x);
memset(y, 0, sizeof y);
memset(h, 0, sizeof h);
memset(st, 0, sizeof st);
memset(sa, 0, sizeof sa);
memset(rk, 0, sizeof rk);
memset(c, 0, sizeof c);
for(int i = 1; i <= n; i++) x[i] = y[i] = h[i] = st[i][0] = sa[i] = rk[i] = 0;
for(int i = 1; i <= m; i++) c[i] = 0;
for(int i = 1; i <= n; i++) c[x[i] = s[i]]++;
for(int i = 1; i <= m; i++) c[i] += c[i - 1];
for(int i = n; i >= 1; i--) sa[c[x[i]]--] = i;
for(int k = 1; k <= n; k <<= 1) {
int num = 0;
for(int i = n - k + 1; i <= n; i++) y[++num] = i;
for(int i = 1; i <= n; i++) if(sa[i] > k) y[++num] = sa[i] - k;
for(int i = 1; i <= m; i++) c[i] = 0;
for(int i = 1; i <= n; i++) c[x[i]]++;
for(int i = 1; i <= m; i++) c[i] += c[i - 1];
for(int i = n; i >= 1; i--) sa[c[x[y[i]]]--] = y[i], y[i] = 0;
std::swap(x, y);
x[sa[1]] = 1, num = 1;
for(int i = 2; i <= n; i++) x[sa[i]] = (y[sa[i]] == y[sa[i - 1]] && y[sa[i] + k] == y[sa[i - 1] + k])? num : ++num;
if(num == n) break;
m = num;
}
for(int i = 1; i <= n; i++) rk[i] = x[i];
int H = 0;
for(int i = 1; i <= n; i++) {
if(rk[i] == 1) continue;
if(H) H--;
int j = sa[rk[i] - 1];
while(i + H <= n && j + H <= n && s[i + H] == s[j + H]) H++;
h[rk[i]] = H;
}
for(int i = 1; i <= n; i++) st[i][0] = h[i];
for(int j = 1; j <= 18; j++)
for(int i = 1; i + (1 << j) - 1 <= n; i++)
st[i][j] = std::min(st[i][j - 1], st[i + (1 << (j - 1))][j - 1]);
}
int LCP(int x, int y) {
x = rk[x], y = rk[y];
if(x > y) std::swap(x, y);
x++;
int l = lg[y - x + 1];
return std::min(st[x][l], st[y - (1 << l) + 1][l]);
}
void ins(char *s, int len) {
n = len, m = 122;
for(int i = 1; i <= n; i++) this->s[i] = s[i];
}
}A, B;
LL pre[maxn], nxt[maxn];
char s[maxn];
int main() {
lg[0] = -1;
for(int i = 1; i < maxn; i++) lg[i] = lg[i >> 1] + 1;
for(int T = in(); T --> 0;) {
scanf("%s", s + 1);
int n = strlen(s + 1);
A.ins(s, n);
std::reverse(s + 1, s + n + 1);
B.ins(s, n);
A.init(), B.init();
memset(pre, 0, sizeof pre), memset(nxt, 0, sizeof nxt);
for(int L = 1; L <= n >> 1; L++)
for(int i = 1; i + L <= n; i += L) {
int j = i + L;
int xx = A.LCP(i, j), yy = B.LCP(n - i + 1, n - j + 1);
xx = std::min(xx, L), yy = std::min(yy, L);
if(xx + yy < L + 1) continue;
int nowlen = xx + yy - L - 1;
nxt[i - yy + 1]++, nxt[i - yy + nowlen + 2]--;
pre[j + xx - nowlen - 1]++, pre[j + xx]--;
}
for(int i = 1; i <= n; i++) pre[i] += pre[i - 1], nxt[i] += nxt[i - 1];
LL ans = 0;
for(int i = 1; i < n; i++) ans += pre[i] * nxt[i + 1];
printf("%lld\n", ans);
}
return 0;
}

P1117 [NOI2016]优秀的拆分的更多相关文章

  1. 并不对劲的bzoj4650:loj2083:uoj219:p1117:[NOI2016]优秀的拆分

    题目大意 "优秀的拆分"指将一个字符串拆分成AABB的形式 十次询问,每次给出一个字符串S(\(|S|\leq3*10^4\)),求它的所有子串的优秀的拆分的方案数之和 题解 此题 ...

  2. UOJ #219 BZOJ 4650 luogu P1117 [NOI2016]优秀的拆分 (后缀数组、ST表)

    连NOI Day1T1都不会做...看了题解都写不出来还要抄Claris的代码.. 题目链接: (luogu)https://www.luogu.org/problemnew/show/P1117 ( ...

  3. Luogu P1117 [NOI2016]优秀的拆分

    题目链接 \(Click\) \(Here\) 这题质量不错,就是暴力分有点足\(hhhhhhhh\),整整有\(95\)分. (搞得我写完暴力都不想写正解直接理解思路之后就直接水过去了\(QwQ\) ...

  4. luogu1117 [NOI2016]优秀的拆分

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

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

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

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

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

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

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

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

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

  9. 题解-NOI2016 优秀的拆分

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

随机推荐

  1. [转]NDK编译库运行时报dlopen failed: cannot locate symbol "__exidx_end" 解决办法

    原文链接:http://blog.csdn.net/acm2008/article/details/41040015 当用NDK编译的库在运行加载时报如下错: dlopen("/data/d ...

  2. ios7 适配

    1.状态栏20px高度问题 ) { [application setStatusBarStyle:UIStatusBarStyleLightContent]; self.window.clipsToB ...

  3. 魔法变量*args 和 **kwargs

    其实并不是必须写成*args 和**kwargs. 只有变量前面的 *(星号)才是必须的. 你也可以写成*var 和**vars. 而写成*args 和**kwargs只是一个通俗的命名约定. *ar ...

  4. [Training Video - 6] [File Reading] [Java] Create and Write Excel File Using Apache POI API

    package com.file.properties; import java.io.File; import java.io.FileNotFoundException; import java. ...

  5. HttpClient 上传/下载文件计算文件传输进度

    1.使用ProgressMessageHandler 获取进度 using namespace System.Net.Http; HttpClientHandler hand = new HttpCl ...

  6. cmake3.2.2 在Ubuntu14.04下的安装

    参考:http://askubuntu.com/questions/610291/how-to-install-cmake-3-2-on-ubuntu-14-04 1.判断相关软件是否安装 sudo  ...

  7. 如何注册facebook应用

    最近项目中要做第三方登录,其中就有facebook的,下面讲解一下如何在facebook中创建应用 1.登录facebook的开发者平台(https://developers.facebook.com ...

  8. C#中深复制和浅复制

    C# 支持两种类型:“值类型”和“引用类型”. 值类型(Value Type)(如 char.int 和 float).枚举类型和结构类型. 引用类型(Reference Type) 包括类 (Cla ...

  9. CKEditor4.x整合教程-Xproer.WordPaster

    版权所有 2009-2017 荆门泽优软件有限公司 保留所有权利 官方网站:http://www.ncmem.com/ 产品首页:http://www.ncmem.com/webplug/wordpa ...

  10. Xshell 连接Linux虚拟机失败解决办法

    当我们在VMWare上安装好Ubuntu虚拟机后,有时候会需要使用多个terminal窗口,但是在虚拟机中多个窗口互相切换查看不方便,这时候用物理机的xshell工具连接到虚拟机中去就显得很方便.直接 ...