UOJ219 NOI2016 优秀的拆分 二分、字符串哈希
题目可以转化为求\(AA\)的数量,设\(cnt1_x\)表示左端点为\(x\)的\(AA\)的数量,\(cnt2_x\)表示右端点为\(x\)的\(AA\)的数量,那么答案就是\(\sum cnt2_i \times cnt1_{i+1}\)
比较朴素的想法是枚举两个后缀然后哈希/SA判断这两个后缀的LCP是否足够长,能够拼成一个\(AA\)形式的串。然后这样就能拿95分???
考虑\(n\)比较大的时候优化枚举。我们对于所有\(len \in [1,\frac{N}{2}]\),在串中标记若干个关键点,两个相邻的关键点的距离为\(len\)。那么一个形如\(AA\)、长度为\(2 \times len\)的串会覆盖恰好\(2\)个关键点,而且两个关键点在覆盖了它的\(A\)串中的位置是一样的。
这意味着对于这两个关键点\(i,j = i+len\),\(min(LCP(suffix_i,suffix_j),len) + min(len,LCS(prefix_i , prefix_j)) > len\)(与\(len\)取\(min\)的原因是不能让覆盖范围超出了\(i,j\)两个点)。这给了我们需要求\(LCP\)与\(LCS\)的信息。SA与二分+Hash均可(反正这题不卡复杂度)。
当然做到上面我们仍然没有优化复杂度……
接下来,考虑算出了\(q=min(LCP(suffix_i,suffix_j),len)\)与\(p=min(len,LCS(prefix_i , prefix_j))\),这意味着串\(s_{[i-p+1 , i + q - 1]}\)与\(s_{[j-p+1,j+q-1]}\)是相等的,而\(j = i + len\)。那么我们随意取出\(s_{[i-p+1 , i + q - 1]}\)的一段长度为\(len\)的段,它的右边都一定紧接着一段长度为\(len\)并且与它相同的段。所以\(cnt1_{i-p+1,i+q-len}\)都会这一步中\(+1\),同时\(cnt2_{i-p+1+2 \times len , i + q + len}\)也会\(+1\)。使用差分数组维护,最后前缀和一下就可以统计答案了。
因为调和级数\(\sum \limits _{i=1}^n \frac{n}{i} < nlogn\),所以总复杂度为\(O(nlogn)\)(使用SA)或者\(O(nlog^2n)\)(使用二分+Hash)
#include<bits/stdc++.h>
#define ll long long
#define PLL pair < long long , long long >
//This code is written by Itst
using namespace std;
inline int read(){
int a = 0;
char c = getchar();
bool f = 0;
while(!isdigit(c) && c != EOF){
if(c == '-')
f = 1;
c = getchar();
}
if(c == EOF)
exit(0);
while(isdigit(c)){
a = a * 10 + c - 48;
c = getchar();
}
return f ? -a : a;
}
const int MAXN = 3e4 + 7 , seed = 131 , MOD1 = 1e9 + 7 , MOD2 = 1e9 + 9;
char s[MAXN];
int L , sum1[MAXN] , sum2[MAXN];
ll Hash[MAXN][2] , poww[MAXN][2] , ans;
inline void init_hash(){
for(int i = 1 ; i <= L ; ++i){
Hash[i][0] = (Hash[i - 1][0] * seed + s[i]) % MOD1;
Hash[i][1] = (Hash[i - 1][1] * seed + s[i]) % MOD2;
}
}
inline PLL get_hash(int l , int r){
return PLL((Hash[r][0] - Hash[l - 1][0] * poww[r - l + 1][0] % MOD1 + MOD1) % MOD1 , (Hash[r][1] - Hash[l - 1][1] * poww[r - l + 1][1] % MOD2 + MOD2) % MOD2);
}
inline int calc_LCP(int p , int q){
int l = 0 , r = min(q - p , L - q + 1);
while(l < r){
int mid = (l + r + 1) >> 1;
get_hash(p , p + mid - 1) == get_hash(q , q + mid - 1) ? l = mid : r = mid - 1;
}
return l;
}
inline int calc_LCS(int p , int q){
int L = 0 , R = min(q - p , p);
while(L < R){
int mid = (L + R + 1) >> 1;
get_hash(p - mid + 1 , p) == get_hash(q - mid + 1 , q) ? L = mid : R = mid - 1;
}
return L;
}
int main(){
#ifndef ONLINE_JUDGE
freopen("in","r",stdin);
//freopen("out","w",stdout);
#endif
poww[0][0] = poww[0][1] = 1;
for(int i = 1 ; i <= 3e4 ; ++i){
poww[i][0] = poww[i - 1][0] * seed % MOD1;
poww[i][1] = poww[i - 1][1] * seed % MOD2;
}
for(int T = read() ; T ; --T){
memset(sum1 , 0 , sizeof(sum1));
memset(sum2 , 0 , sizeof(sum2));
scanf("%s" , s + 1);
L = strlen(s + 1);
init_hash();
for(int i = 1 ; i < L ; ++i)
for(int j = 1 ; j + i <= L ; j += i){
int p = calc_LCS(j , j + i) , q = calc_LCP(j , j + i);
if(p + q - 1 >= i){
++sum1[j - p + 1];
--sum1[j + q - i + 1];
++sum2[j - p + 1 + 2 * i - 1];
--sum2[j + q + i];
}
}
for(int i = 1 ; i <= L ; ++i){
sum1[i] += sum1[i - 1];
sum2[i] += sum2[i - 1];
}
ans = 0;
for(int i = 1 ; i < L ; ++i)
ans += sum2[i] * sum1[i + 1];
cout << ans << endl;
}
return 0;
}
UOJ219 NOI2016 优秀的拆分 二分、字符串哈希的更多相关文章
- BZOJ4650/UOJ219 [Noi2016]优秀的拆分
本文版权归ljh2000和博客园共有,欢迎转载,但须保留此声明,并给出原文链接,谢谢合作. 本文作者:ljh2000 作者博客:http://www.cnblogs.com/ljh2000-jump/ ...
- [UOJ#219][BZOJ4650][Noi2016]优秀的拆分
[UOJ#219][BZOJ4650][Noi2016]优秀的拆分 试题描述 如果一个字符串可以被拆分为 AABBAABB 的形式,其中 A 和 B 是任意非空字符串,则我们称该字符串的这种拆分是优秀 ...
- [NOI2016]优秀的拆分(SA数组)
[NOI2016]优秀的拆分 题目描述 如果一个字符串可以被拆分为 \(AABB\) 的形式,其中 A和 B是任意非空字符串,则我们称该字符串的这种拆分是优秀的. 例如,对于字符串 \(aabaaba ...
- 题解-NOI2016 优秀的拆分
NOI2016 优秀的拆分 \(T\) 组测试数据.求字符串 \(s\) 的所有子串拆成 \(AABB\) 形式的方案总和. 数据范围:\(1\le T\le 10\),\(1\le n\le 3\c ...
- [NOI2016]优秀的拆分&&BZOJ2119股市的预测
[NOI2016]优秀的拆分 https://www.lydsy.com/JudgeOnline/problem.php?id=4650 题解 如果我们能够统计出一个数组a,一个数组b,a[i]表示以 ...
- luogu1117 [NOI2016]优秀的拆分
luogu1117 [NOI2016]优秀的拆分 https://www.luogu.org/problemnew/show/P1117 后缀数组我忘了. 此题哈希可解决95分(= =) 设\(l_i ...
- 【BZOJ4560】[NOI2016]优秀的拆分
[BZOJ4560][NOI2016]优秀的拆分 题面 bzoj 洛谷 题解 考虑一个形如\(AABB\)的串是由两个形如\(AA\)的串拼起来的 那么我们设 \(f[i]\):以位置\(i\)为结尾 ...
- 并不对劲的bzoj4650:loj2083:uoj219:p1117:[NOI2016]优秀的拆分
题目大意 "优秀的拆分"指将一个字符串拆分成AABB的形式 十次询问,每次给出一个字符串S(\(|S|\leq3*10^4\)),求它的所有子串的优秀的拆分的方案数之和 题解 此题 ...
- [BZOJ]4650: [Noi2016]优秀的拆分
Time Limit: 30 Sec Memory Limit: 512 MB Description 如果一个字符串可以被拆分为 AABBAABB 的形式,其中 AA 和 BB 是任意非空字符串, ...
随机推荐
- 能用HTML/CSS解决的问题,就不要用JS
原因:简单. 简单就意味着更快的开发速度,更小的维护成本,同时往往具有更好的体验. 一,导航高亮 效果图: 代码: <!DOCTYPE html> <html lang=" ...
- Expo大作战(三十六)--expo sdk api之 ImagePicker,ImageManipulator,Camera
简要:本系列文章讲会对expo进行全面的介绍,本人从2017年6月份接触expo以来,对expo的研究断断续续,一路走来将近10个月,废话不多说,接下来你看到内容,讲全部来与官网 我猜去全部机翻+个人 ...
- Spring Boot Actuator认识
概述 spring-boot-starter-actuator:是一个用于暴露自身信息的模块,主要用于监控与管理. 为了保证actuator暴露的监控接口的安全性,需要添加安全控制的依赖spring- ...
- MVC| 路由测试代码
using System; using Microsoft.VisualStudio.TestTools.UnitTesting; using Moq; using System.Web; using ...
- Asp.Net配置不允许通过url方式访问目录下的资源
Asp.Net网站发布后,有部分文件为了安全性,是不能直接通过url访问获取 通常有2种做法: 1.将文件目录建立在 App_code 或者App_Data 等默认的隐藏目录下 2.将文件的目录添加到 ...
- C#语言————第三章 使用属性升级MyBank
********常见的访问修饰符*********: public :公共的,可以在其他类中访问 private:私有的,只有在本类里可以使用,其他的类无权访问 类的默认访问修饰符 internal( ...
- Oracle 表操作(转)
1.增加新字段:alter table table_name add (name varchar(20) default 'http://www.zangjing.net/');. 2.修改表字段:a ...
- 【PAT】B1066 图像过滤(15 分)
注意输出是占三位,其他的挺水 #include<stdio.h> #include<algorithm> using namespace std; int main(){ in ...
- Spring boot 之 dubbo 无xml 简单入门
Dubbo简介 Dubbo框架设计一共划分了10个层,而最上面的Service层是留给实际想要使用Dubbo开发分布式服务的开发者实现业务逻辑的接口层.图中左边淡蓝背景的为服务消费方使用的接口,右边淡 ...
- 月球美容计划之最小生成树(MST)
寒假学的两个算法,普里姆,克鲁斯卡尔最终弄明确了.能够发总结了 先说说普里姆,它的本质就是贪心.先从随意一个点開始,找到最短边,然后不断更新更新len数组,然后再选取最短边并标记经过的点,直到全部的点 ...