BZOJ4650 [NOI2016]优秀的拆分 【后缀数组】
题目
如果一个字符串可以被拆分为 AABBAABB 的形式,其中 AA 和 BB 是任意非空字符串,则我们称该字符串的这种拆
分是优秀的。例如,对于字符串 aabaabaa,如果令 A=aabA=aab,B=aB=a,我们就找到了这个字符串拆分成 AABBA
ABB 的一种方式。一个字符串可能没有优秀的拆分,也可能存在不止一种优秀的拆分。比如我们令 A=aA=a,B=baa
B=baa,也可以用 AABBAABB 表示出上述字符串;但是,字符串 abaabaa 就没有优秀的拆分。现在给出一个长度为
nn 的字符串 SS,我们需要求出,在它所有子串的所有拆分方式中,优秀拆分的总个数。这里的子串是指字符串
中连续的一段。以下事项需要注意:出现在不同位置的相同子串,我们认为是不同的子串,它们的优秀拆分均会被
记入答案。在一个拆分中,允许出现 A=BA=B。例如 cccc 存在拆分 A=B=cA=B=c。字符串本身也是它的一个子串。
输入格式
每个输入文件包含多组数据。输入文件的第一行只有一个整数 TT,表示数据的组数。保证 1≤T≤101≤T≤10。接
下来 TT 行,每行包含一个仅由英文小写字母构成的字符串 SS,意义如题所述。
输出格式
输出 TT 行,每行包含一个整数,表示字符串 SS 所有子串的所有拆分中,总共有多少个是优秀的拆分。
输入样例
4
aabbbb
cccccc
aabaabaabaa
bbaabaababaaba
输出样例
3
5
4
7
提示
我们用 S[i,j]S[i,j] 表示字符串 SS 第 ii 个字符到第 jj 个字符的子串(从 11 开始计数)。第一组数据中,
共有 33 个子串存在优秀的拆分:S[1,4]=aabbS[1,4]=aabb,优秀的拆分为 A=aA=a,B=bB=b;S[3,6]=bbbbS[3,6]
=bbbb,优秀的拆分为 A=bA=b,B=bB=b;S[1,6]=aabbbbS[1,6]=aabbbb,优秀的拆分为 A=aA=a,B=bbB=bb。而剩
下的子串不存在优秀的拆分,所以第一组数据的答案是 33。第二组数据中,有两类,总共 44 个子串存在优秀的
拆分:对于子串 S[1,4]=S[2,5]=S[3,6]=ccccS[1,4]=S[2,5]=S[3,6]=cccc,它们优秀的拆分相同,均为 A=cA=c,
B=cB=c,但由于这些子串位置不同,因此要计算 33 次;对于子串 S[1,6]=ccccccS[1,6]=cccccc,它优秀的拆分
有 22 种:A=cA=c,B=ccB=cc 和 A=ccA=cc,B=cB=c,它们是相同子串的不同拆分,也都要计入答案。所以第二组
数据的答案是 3+2=53+2=5。第三组数据中,S[1,8]S[1,8] 和 S[4,11]S[4,11] 各有 22 种优秀的拆分,其中 S[1
,8]S[1,8] 是问题描述中的例子,所以答案是 2+2=42+2=4。第四组数据中,S[1,4]S[1,4],S[6,11]S[6,11],S[7
,12]S[7,12],S[2,11]S[2,11],S[1,8]S[1,8] 各有 11 种优秀的拆分,S[3,14]S[3,14] 有 22 种优秀的拆分,
所以答案是 5+2=75+2=7。
题解
我们设\(f[i]\)为以\(i\)为结尾的\(AA\)串的数量
设\(g[i]\)为以\(i\)开头的\(AA\)串的数量
那么
\]
所以我们只要找出所有\(AA\)串即可
根据后缀数组的套路,为找出所有\(AA\)串,我们枚举\(A\)的长度\(L\),然后每隔\(L\)设一个监测点,如图:
其中圈起来的就是中间那个监测点所管辖的\(len = 3\)的子串
如此,如果存在长度为\(2 * L\)的\(AA\)串,那么相邻的\(A\)中一定有且仅有一个相邻的监测点
我们就枚举相邻的两个监测点,比较它们的lcp和往前的lcp大小,就可以确定它们管辖的串那些可以匹配
具体对正串反串分别求一次SA【或者并在一起求】,做到\(O(1)\)询问lcp
然后用一个差分数组维护\(f[i]\)和\(g[i]\)
最后统计答案就做完了
时间复杂度\(O(nlogn + \sum\limits_{L = 1}^{n} \frac{n}{L}) = O(nlogn + n * \sum\limits_{L = 1}^{n} \frac{1}{L}) = O(nlogn)\)
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#define LL long long int
#define Redge(u) for (int k = h[u],to; k; k = ed[k].nxt)
#define REP(i,n) for (int i = 1; i <= (n); i++)
#define cls(s) memset(s,0,sizeof(s))
using namespace std;
const int maxn = 100005,maxm = 100005,INF = 1000000000;
inline int read(){
int out = 0,flag = 1; char c = getchar();
while (c < 48 || c > 57){if (c == '-') flag = -1; c = getchar();}
while (c >= 48 && c <= 57){out = (out << 3) + (out << 1) + c - 48; c = getchar();}
return out * flag;
}
char s[maxn];
int N,n,m,sa[maxn],rank[maxn],height[maxn],t1[maxn],t2[maxn],bac[maxn];
int mn[maxn][18],bin[30],Log[maxn];
void getsa(){
int *x = t1,*y = t2; m = 1000;
for (int i = 0; i <= m; i++) bac[i] = 0;
for (int i = 1; i <= n; i++) bac[x[i] = s[i]]++;
for (int i = 1; i <= m; i++) bac[i] += bac[i - 1];
for (int i = n; i; i--) sa[bac[x[i]]--] = i;
for (int k = 1; k <= n; k <<= 1){
int p = 0;
for (int i = n - k + 1; i <= n; i++) y[++p] = i;
for (int i = 1; i <= n; i++) if (sa[i] - k > 0) y[++p] = sa[i] - k;
for (int i = 0; i <= m; i++) bac[i] = 0;
for (int i = 1; i <= n; i++) bac[x[y[i]]]++;
for (int i = 1; i <= m; i++) bac[i] += bac[i - 1];
for (int i = n; i; i--) sa[bac[x[y[i]]]--] = y[i];
swap(x,y);
x[sa[1]] = p = 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] ? p : ++p);
if (p >= n) break;
m = p;
}
for (int i = 1; i <= n; i++) rank[sa[i]] = i;
for (int i = 1,k = 0; i <= n; i++){
if (k) k--;
int j = sa[rank[i] - 1];
while (s[i + k] == s[j + k]) k++;
height[rank[i]] = k;
}
for (int i = 1; i <= n; i++) mn[i][0] = height[i];
REP(j,17) REP(i,n){
if (i + bin[j] - 1 > n) break;
mn[i][j] = min(mn[i][j - 1],mn[i + bin[j - 1]][j - 1]);
}
}
int lcp(int a,int b){
int l = rank[a],r = rank[b];
if (l > r) swap(l,r); l++;
int t = Log[r - l + 1];
return min(mn[l][t],mn[r - bin[t] + 1][t]);
}
int pre_lcp(int a,int b){
int l = rank[N - a + 1],r = rank[N - b + 1];
if (l > r) swap(l,r); l++;
int t = Log[r - l + 1];
return min(mn[l][t],mn[r - bin[t] + 1][t]);
}
LL f[maxn],g[maxn];
void solve(){
memset(f,0,sizeof(f));
memset(g,0,sizeof(g));
for (int L = 1; L <= (n >> 1); L++){
for (int a = L,b = a + L,l,r,lenl,lenr,len; b <= n; a += L,b += L){
lenl = min(pre_lcp(a,b),L);
lenr = min(lcp(a,b),L);
len = lenl + lenr - 1;
l = a - lenl + 1; r = l + len - L;
if (l <= r) g[l]++,g[r + 1]--;
l = b - lenl + L; r = l + len - L;
if (l <= r) f[l]++,f[r + 1]--;
}
}
REP(i,n) g[i] += g[i - 1],f[i] += f[i - 1];
//REP(i,n) printf("%lld",f[i]); puts("");
//REP(i,n) printf("%lld",g[i]); puts("");
LL ans = 0;
for (int i = 2; i < n - 1; i++){
ans += f[i] * g[i + 1];
}
printf("%lld\n",ans);
}
int main(){
bin[0] = 1; for (int i = 1; i <= 25; i++) bin[i] = bin[i - 1] << 1;
Log[0] = -1; for (int i = 1; i < maxn; i++) Log[i] = Log[i >> 1] + 1;
int T = read();
while (T--){
cls(s); cls(t1); cls(t2);
scanf("%s",s + 1); n = strlen(s + 1);
s[n + 1] = '#';
for (int i = 1; i <= n; i++) s[n + 1 + i] = s[n - i + 1];
N = n = n << 1 | 1;
getsa();
n >>= 1;
solve();
}
return 0;
}
BZOJ4650 [NOI2016]优秀的拆分 【后缀数组】的更多相关文章
- [NOI2016]优秀的拆分 后缀数组
题面:洛谷 题解: 因为对于原串的每个长度不一定等于len的拆分而言,如果合法,它将只会被对应的子串统计贡献. 所以子串这个限制相当于是没有的. 所以我们只需要对于每个位置i求出f[i]表示以i为开头 ...
- BZOJ.4650.[NOI2016]优秀的拆分(后缀数组 思路)
BZOJ 洛谷 令\(st[i]\)表示以\(i\)为开头有多少个\(AA\)这样的子串,\(ed[i]\)表示以\(i\)结尾有多少个\(AA\)这样的子串.那么\(Ans=\sum_{i=1}^{ ...
- UOJ #219 BZOJ 4650 luogu P1117 [NOI2016]优秀的拆分 (后缀数组、ST表)
连NOI Day1T1都不会做...看了题解都写不出来还要抄Claris的代码.. 题目链接: (luogu)https://www.luogu.org/problemnew/show/P1117 ( ...
- BZOJ 4650 [Noi2016]优秀的拆分 ——后缀数组
我们只需要统计在某一个点开始的形如$AA$字符串个数,和结束的个数相乘求和. 首先枚举循环节的长度L.即$\mid (A) \mid=L$ 然后肯定会经过s[i]和[i+L]至少两个点. 然后我们可以 ...
- [UOJ#219][BZOJ4650][Noi2016]优秀的拆分
[UOJ#219][BZOJ4650][Noi2016]优秀的拆分 试题描述 如果一个字符串可以被拆分为 AABBAABB 的形式,其中 A 和 B 是任意非空字符串,则我们称该字符串的这种拆分是优秀 ...
- [NOI2016]优秀的拆分(SA数组)
[NOI2016]优秀的拆分 题目描述 如果一个字符串可以被拆分为 \(AABB\) 的形式,其中 A和 B是任意非空字符串,则我们称该字符串的这种拆分是优秀的. 例如,对于字符串 \(aabaaba ...
- bzoj千题计划317:bzoj4650: [Noi2016]优秀的拆分(后缀数组+差分)
https://www.lydsy.com/JudgeOnline/problem.php?id=4650 如果能够预处理出 suf[i] 以i结尾的形式为AA的子串个数 pre[i] 以i开头的形式 ...
- UOJ#219. 【NOI2016】优秀的拆分 [后缀数组 ST表]
#219. [NOI2016]优秀的拆分 题意:求有多少AABB样子的子串,拆分不同的同一个子串算多个 一开始一直想直接求,并不方便 然后看了一眼Claris的题解的第一行就有思路了 如果分开,求\( ...
- UOJ#219/BZOJ4650 [NOI2016]优秀的拆分 字符串 SA ST表
原文链接http://www.cnblogs.com/zhouzhendong/p/9025092.html 题目传送门 - UOJ#219 (推荐,题面清晰) 题目传送门 - BZOJ4650 题意 ...
随机推荐
- 2017.12.19 Java包的静态导入import static和import的区别
import static静态导入是JDK1.5中的新特性.一般我们导入一个类都用 import com-..ClassName;而静态导入是这样:import static com-..ClassN ...
- python基础一 day17 初识递归
#递归函数 # 了解什么是递归 : 在函数中调用自身函数 # 最大递归深度默认是997/998 —— 是python从内存角度出发做得限制 # 能看懂递归 # 能知道递归的应用场景 # 初识递归 —— ...
- python读取图像
from PIL import Imageimg = Image.open('/Users/NaCl/Desktop/test.png')img.show()
- “System.AccessViolationException”类型的未经处理的异常在 System.Data.dll 中发生 其他信息: 尝试读取或写入受保护的内存。这通常指示其他内存已损坏
用管理员身份运行CMD:netsh winsock reset就可以解决
- Websocket教程SpringBoot+Maven整合
1.大话websocket及课程介绍 简介: websocket介绍.使用场景分享.学习课程需要什么基础 2.课程技术选型和浏览器兼容讲解 简介: 简单介绍什么是springboot.socketjs ...
- 【算法】Fibonacci(斐波那契数列)相关问题
一.列出Fibonacci数列的前N个数 using System; using System.Collections.Generic; using System.Linq; using System ...
- 第17题:打印1到最大的n位数
面试题17:打印1到最大的n位数 题目:输入数字n,按顺序打印出从1最大的n位十进制数.比如输入3,则打印出1.2.3一直到最大的3位数即999. 考点: 用字符串或者数组表达一个大数. 思路 1. ...
- SummerVocation_Learning--java的String 类
java中String属于java.lang的package包,是一个类.代表不可变的字符序列. String类的常见构造方法: String(String original),创建一个对象为orig ...
- (78)zabbix值缓存(value cache)说明
在zabbix-2.2版本之前,zabbix计算trigger与calculated/aggregate值都是直接通过sql语句查询并处理出来的结果,为了提高这块的性能与效率,zabbix引入了val ...
- MySql学习笔记02
MySql02 复习 数据库相关 连接数据库的命令 mysql -uroot -p 创建数据库 create database db2; 查询所有的数据库 show databases; 查询单个数据 ...