题目描述

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

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

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

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

以下事项需要注意:

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

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

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

输入输出格式

输入格式:

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

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

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

输出格式:

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

输入输出样例

输入样例#1: 复制

4
aabbbb
cccccc
aabaabaabaa
bbaabaababaaba
输出样例#1: 复制

3
5
4
7

说明

我们用S_{i,j}Si,j​表示字符串 SS第 ii个字符到第jj个字符的子串(从11开始计数)。

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

S_{1,4}=aabbS1,4​=aabb,优秀的拆分为A=aA=a,B=bB=b;

S_{3,6}=bbbbS3,6​=bbbb,优秀的拆分为 A=bA=b,B=bB=b;

S_{1,6}=aabbbbS1,6​=aabbbb,优秀的拆分为 A=aA=a,B=bbB=bb。

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

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

对于子串 S_{1,4}=S_{2,5}=S_{3,6}=ccccS1,4​=S2,5​=S3,6​=cccc,它们优秀的拆分相同,均为A=cA=c,B=cB=c,但由于这些子串位置不同,因此要计算33 次;

对于子串 S_{1,6}=ccccccS1,6​=cccccc,它优秀的拆分有 22种:A=cA=c,B=ccB=cc和 A=ccA=cc,B=cB=c,它们是相同子串的不同拆分,也都要计入答案。

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

第三组数据中,S_{1,8}S1,8​和 S_{4,11}S4,11​ 各有 22 种优秀的拆分,其中S_{1,8}S1,8​ 是问题描述中的例子,所以答案是2+2=42+2=4。

第四组数据中,S_{1,4},S_{6,11},S_{7,12},S_{2,11},S_{1,8}S1,4​,S6,11​,S7,12​,S2,11​,S1,8​ 各有 11种优秀的拆分,S_{3,14}S3,14​ 有22 种优秀的拆分,所以答案是 5+2=75+2=7。

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

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

暴力Hash 80分TLE

枚举子串,枚举A的长度,hash比较

 #include <iostream>
#include <set>
#include <cmath>
#include <stdio.h>
#include <cstring>
#include <algorithm>
#include <vector>
#include <queue>
#include <map>
using namespace std;
typedef long long LL;
#define inf 0x7f7f7f7f const int maxn = 3e4 + ; int t;
char s[maxn];
unsigned long long h[maxn], p[maxn];
int get_hash(int i, int j)
{
return h[j] - h[i - ] * p[j - i + ];
} int main()
{
scanf("%d", &t);
p[] = ;
for(int i = ; i < maxn; i++){
p[i] = p[i - ] * ;
}
while(t--){
scanf("%s", s + );
int len = strlen(s + );
for(int i = ; i <= len; i++){
h[i] = h[i - ] * + s[i] - 'a' + ;
} int ans = ;
for(int i = ; i <= len; i++){
for(int j = i + ; j <= len; j += ){
int l = j - i + ;
for(int x = ; x <= l / - ; x++){
int y = l / - x;
if(get_hash(i, i + x - ) == get_hash(i + x, i + * x - )
&& get_hash(i + * x, i + * x + y - ) == get_hash(i + * x + y, i + l - )){
ans++;
}
}
}
}
printf("%d\n", ans);
} return ;
}

稍微优化了一下的暴力Hash 95分TLE

用l[i]表示以i为结尾的满足AA串的个数, r[i]表示以i+1为开头的满足BB串的个数

 #include <iostream>
#include <set>
#include <cmath>
#include <stdio.h>
#include <cstring>
#include <algorithm>
#include <vector>
#include <queue>
#include <map>
using namespace std;
typedef long long LL;
#define inf 0x7f7f7f7f const int maxn = 3e4 + ; int t;
char s[maxn];
unsigned long long h[maxn], p[maxn];
int l[maxn], r[maxn];
int get_hash(int i, int j)
{
return h[j] - h[i - ] * p[j - i + ];
} int main()
{
scanf("%d", &t);
p[] = ;
for(int i = ; i < maxn; i++){
p[i] = p[i - ] * ;
}
while(t--){
scanf("%s", s + );
int len = strlen(s + );
for(int i = ; i <= len; i++){
h[i] = h[i - ] * + s[i] - 'a' + ;
} int ans = ;
for(int i = ; i <= len; i++){
l[i] = r[i] = ;
for(int j = i / ; j >= ; j--){
if(get_hash(i - * j + , i - j) == get_hash(i - j + , i)){
l[i]++;
}
}
for(int j = (len - i) / ; j >= ; j--){
if(get_hash(i + , i + j) == get_hash(i + j + , i + * j)){
r[i]++;
}
}
}
for(int i = ; i <= len; i++){
ans += l[i] * r[i];
}
printf("%d\n", ans);
} return ;
}

还需要继续优化一下找l和r数组的过程,枚举AA串的一半长度L,把整个字符串都分成长为L的好几段。

比如我们现在看第i段的起始点,第i段和第i-1段找一个最长公共前缀,i-1段和第i-2段找一个最长公共后缀。

如果lcs和lcp的长度大于L的话,说明是存在这样一个AA串的【画个图就能理解了】

如果lcs+lcp=L,那么恰好有这么一个串,如果大于L,那么重叠多少,就有多少个这样的串。

用差分的思想进行统计。本来是这个区间都需要++的,现在只给这个区间的开头++,最后求和。那么区间结尾之后的都是多加了的,就给他--

要注意head应该是在[i-l~i]区间, tail在[i-i+L]区间,最后答案要使用longlong

还有题目给的数据范围3e4是不够的,会RE。

 #include <iostream>
#include <set>
#include <cmath>
#include <stdio.h>
#include <cstring>
#include <algorithm>
#include <vector>
#include <queue>
#include <map>
using namespace std;
typedef long long LL;
#define inf 0x7f7f7f7f const int maxn = 5e4 + ; int t;
char s[maxn];
unsigned long long h[maxn], p[maxn];
LL u[maxn], v[maxn];
unsigned long long get_hash(int i, int j)
{
return h[i] - h[j] * p[j - i];
//return h[j] - h[i - 1] * p[j - i + 1];
} int main()
{
scanf("%d", &t);
p[] = ;
for(int i = ; i < maxn; i++){
p[i] = p[i - ] * ;
}
while(t--){
//memset(u, 0, sizeof(u));
//memset(v, 0, sizeof(v));
scanf("%s", s + );
int len = strlen(s + );
h[len + ] = ;
for(int i = len; i >= ; i--){
u[i] = v[i] = ;
h[i] = h[i + ] * + s[i] - 'a' + ;
}
/*for(int i = 1; i <= len; i++){
u[i] = v[i] = 0;
h[i] = h[i - 1] * 131 + s[i] - 'a' + 1;
}*/ for(int l = ; l * <= len; l++){//枚举A的长度
for(int i = l + l; i <= len; i += l){//分块
if(s[i] != s[i - l]){
continue;
}
//与上上个块求最长公共后缀
int st = , ed = l, last = i - l, pos = ;
while(st <= ed){
int mid = (st + ed) / ;
if(get_hash(last - mid + , last + ) == get_hash(i - mid + , i + )){
st = mid + ;
pos = mid;
}
else{
ed = mid - ;
}
}
int head = i - pos + ; //与上一个块求最长公共前缀
st = ;ed = l; pos = ;
while(st <= ed){
int mid = (st + ed) / ;
if(get_hash(last, last + mid) == get_hash(i, i + mid)){
st = mid + ;
pos = mid;
}
else{
ed = mid - ;
}
}
int tail = i + pos - ;
head = max(head + l - , i);
tail = min(tail, i + l - );
if(head <= tail){
u[head - l * + ]++;
u[tail + - * l + ]--;
v[head]++;
v[tail + ]--;
}
}
} LL ans = ;
for(int i = ; i <= len; i++){
u[i] += u[i - ];
v[i] += v[i - ];
}
for(int i = ; i < len; i++){
ans += v[i] * u[i + ];
}
printf("%lld\n", ans);
} return ;
}

洛谷P1117 优秀的拆分【Hash】【字符串】【二分】【好难不会】的更多相关文章

  1. 洛谷P1117 优秀的拆分

    题意:求一个字符串中有多少形如AABB的子串. 解:嗯...我首先极度SB的想了一个后缀自动机套线段树启发式合并的做法,想必会TLE. 然后跑去看题解,发现实在是妙不可言... 显然要对每个位置求出向 ...

  2. bzoj 4650 & 洛谷 P1117 优秀的拆分 —— 枚举关键点+后缀数组

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

  3. 洛谷 P2404 自然数的拆分问题

    题目链接 https://www.luogu.org/problemnew/show/P2404 题目背景 木有...... 题目描述 任何一个大于1的自然数n,总可以拆分成若干个小于n的自然数之和. ...

  4. 洛谷 P4036 [JSOI2008]火星人(splay+字符串hash)

    题面 洛谷 题解 首先,我们知道求最长公共前缀可以用二分答案+hash来求 因为有修改操作, 考虑将整个字符串的hash值放入splay中 接着就是splay的基本操作了 Code #include& ...

  5. 洛谷P1117 棋盘游戏

    洛谷1117 棋盘游戏 题目描述 在一个4*4的棋盘上有8个黑棋和8个白棋,当且仅当两个格子有公共边,这两个格子上的棋是相邻的.移动棋子的规则是交换相邻两个棋子.现在给出一个初始棋盘和一个最终棋盘,要 ...

  6. 洛谷 P3263 [JLOI2015]有意义的字符串

    洛谷 首先,看到\((\frac{(b+\sqrt{d})}{2})^n\),很快能够想到一元二次方程的解\(\frac{-b\pm\sqrt{\Delta}}{2a}\). 所以可以推出,\(\fr ...

  7. 洛谷P2402 奶牛隐藏(网络流,二分答案,Floyd)

    洛谷题目传送门 了解网络流和dinic算法请点这里(感谢SYCstudio) 题目 题目背景 这本是一个非常简单的问题,然而奶牛们由于下雨已经非常混乱,无法完成这一计算,于是这个任务就交给了你.(奶牛 ...

  8. 洛谷P4072 [SDOI2016]征途(带权二分,斜率优化)

    洛谷题目传送门 一开始肯定要把题目要求的式子给写出来 我们知道方差的公式\(s^2=\frac{\sum\limits_{i=1}^{m}(x_i-\overline x)^2}{m}\) 题目要乘\ ...

  9. 洛谷P4344 脑洞治疗仪 [SHOI2015] 线段树+二分答案/分块

    !!!一道巨恶心的数据结构题,做完当场爆炸:) 首先,如果你用位运算的时候不小心<<打成>>了,你就可以像我一样陷入疯狂的死循环改半个小时 然后,如果你改出来之后忘记把陷入死循 ...

随机推荐

  1. [Arch] 02. Design principle and Software Pattern

    Ref: 软件设计的七大原则 有时间的话,还需进一步深入理解. Figure, 重要的前五个原则 单一职责原则 (Simple responsibility pinciple SRP) 类的设计趋向于 ...

  2. C# winform开发嵌套Chrome内核浏览器(WebKit.net)开发(一)

    https://www.cnblogs.com/Maxq/p/6566558.html WebKit.net是对WebKit的.Net封装, 使用它.net程序可以非常方便的集成和使用webkit作为 ...

  3. 时间戳Id

    ID:格式据1970.1.1毫秒数 1535091029740  13位 问题:高并发,分布式明显会有问题,网上有雪花算法,但是位数跟我需要的不一样,暂时不考虑,以后研究再用. 参考: https:/ ...

  4. MVC5 Entity Framework学习之实现主要的CRUD功能

    在上一篇文章中,我们使用Entity Framework 和SQL Server LocalDB创建了一个MVC应用程序,并使用它来存储和显示数据.在这篇文章中,你将对由 MVC框架自己主动创建的CR ...

  5. flexbox父盒子flex-wrap属性

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  6. FFmpeg X264的preset和tune

    鉴于x264的参数众多,各种参数的配合复杂,为了使用者方便,x264建议如无特别需要可使用preset和tune设置.这套开发者推荐的参数较为合理,可在此基础上在调整一些具体参数以符合自己需要,手动设 ...

  7. RF采用SSHLibary库执行sudo命令,提示sudo: sorry, you must have a tty to run sudo错误的解决办法

    经了解Execute Command and Start Command两个关键字执行linux命令会新增一个shell,并且可能改变环境配置,如果要确保环境不被改变,则需采用Write和Read方法 ...

  8. MHL相关资源链接

    http://www.mhlconsortium.org/ 消费者网站: www.meetmhl.com采用者网站:www.mhltech.org 博客http://blog.sina.com.cn/ ...

  9. linux C 调用shell程序执行

    #include<stdio.h> #include <unistd.h> #include <sys/types.h> #include <stdlib.h ...

  10. Android学习之Spinner

    Android给我们提供了一个spinner控件,这个控件主要就是一个列表,那么我们就来说说这个控件吧,这个控件在以前的也看见过,但今天还是从新介绍一遍吧.Spinner位于 android.widg ...