[NOI2016]优秀的拆分 后缀数组
题面:洛谷
题解:
因为对于原串的每个长度不一定等于len的拆分而言,如果合法,它将只会被对应的子串统计贡献。
所以子串这个限制相当于是没有的。
所以我们只需要对于每个位置i求出f[i]表示以i为开头的形如BB这样的串的个数,g[i]表示以i为结尾的形如AA这样的串的个数即可。
考虑分别处理这2个数组。
我们可以枚举AA(BB)这样的串中A(B)的长度l,然后把原串每l个字符放在一个块中,在考虑统计答案。
先考虑这样一个问题:
假如固定一个串的结尾,再枚举这个串A的长度,怎样可以判断是否合法?
实际上我们只需要判断我们假定的这个AA串的开头和中间位置(结尾向前走A的长度)的LCP是否可以覆盖开头到中间即可。
然后如果我们已经把原串对于当前枚举的长度l分成了很多块,其实我们就已经可以对与每个块的开头结尾所代表的点对(i, j)判断是否可以产生贡献了。
但是怎么统计 其他没有刚好对应在某个块的开头结尾的点对 的贡献呢?
表示并没有想出来,,,但是感觉有个blog写的很好,,,
推荐一下:[BZOJ]4650 优秀的拆分(Noi2016)
以后彻底搞懂了再来填坑吧。
#include<bits/stdc++.h>
using namespace std;
#define R register int
#define AC 301000
#define ac 602000
#define LL long long
#define rev reverse
#define mem(x) memset(x, 0, sizeof(x)) int T, n, m;
int h[ac], sa[ac], p1[ac], p2[ac], b[ac], d[ac];
int rk[ac], p[AC], t[AC], rk1[ac];
int st1[AC][], st2[AC][];
int f[AC], g[AC];
LL ans;
char s[AC]; void init()
{
for(R i = ; i <= n; i ++) f[i] = g[i] = rk[i] = ;//因为有多组数据,所以要全部清空
}//mem这么多次还不如for inline void upmax(int &a, int b){
if(b > a) a = b;
} inline int Min(int a, int b){
return (a < b) ? a : b;
} void pre()
{
scanf("%s", s + ), n = strlen(s + ), m = ;
} void ssort()
{
for(R i = ; i <= n; i ++) ++ d[p2[i]];
for(R i = ; i <= m; i ++) d[i] += d[i - ];
for(R i = ; i <= n; i ++) b[d[p2[i]] --] = i;//给i分配d[p2[i]]的排名
for(R i = ; i <= m; i ++) d[i] = ; for(R i = ; i <= n; i ++) ++ d[p1[i]];
for(R i = ; i <= m; i ++) d[i] += d[i - ];
for(R i = n; i; -- i) sa[d[p1[b[i]]] --] = b[i];//给b[i]分配d[p1[b[i]]]的排名
for(R i = ; i <= m; i ++) d[i] = ;
} void get_sa()
{
for(R i = ; i <= n; i ++) sa[i] = i, rk[i] = s[i];//初始化
m = ;//这个也要重置
for(R k = ; k <= n; k <<= )
{
for(R i = ; i <= n; i ++) p1[i] = rk[i], p2[i] = rk[i + k];
ssort();
int tmp = ;
rk[sa[]] = ;
for(R i = ; i <= n; i ++)
rk[sa[i]] = (p1[sa[i]] == p1[sa[i - ]] && p2[sa[i]] == p2[sa[i - ]]) ? tmp : ++ tmp;
if(tmp >= n) break;
m = tmp;//忘了,,,
}
} void build()//获取h数组
{
//memset(h, 0, sizeof(h));
for(R i = , k = ; i <= n; i ++)
{
if(k) -- k;
int j = sa[rk[i] - ];
while(s[i + k] == s[j + k]) ++ k;
h[rk[i]] = k;
}
} #define st st1
void build1()//建st1(维护LCP)
{
int tmp = , cnt = ;
memcpy(rk1, rk, sizeof(rk));
for(R i = ; i <= n; i ++)
{
st[i][] = h[i];
if(i == tmp << ) tmp <<= , ++ cnt;
p[i] = tmp, t[i] = cnt;
}
}
#undef st void build2()//建st2(维护LCS)改成st1, st2一起建了。。。。
{
for(R i = ; i <= n; i ++) st2[i][] = h[i];
int tmp = ;
for(R i = ; i <= ; i ++)
{
for(R j = ; j <= n; j ++)
{
st1[j][i] = Min(st1[j][i - ], st1[j + tmp][i - ]);
st2[j][i] = Min(st2[j][i - ], st2[j + tmp][i - ]);
}
tmp <<= ;
}
} inline void swap(int &l, int &r)
{
int x = l;
l = r, r = x;
} int get1(int l, int r)//查询串l和串r的LCP
{
l = rk1[l], r = rk1[r];
if(l > r) swap(l, r);
++ l;
int len = r - l + ;
return Min(st1[l][t[len]], st1[r - p[len] + ][t[len]]);
} int get2(int l, int r)//查询串l和串r的LCS
{//因为是翻转过来求的,所以查询要翻转一下
l = n - l + , r = n - r + ;
l = rk[l], r = rk[r];
if(l > r) swap(l, r);
++ l;
int len = r - l + ;
return Min(st2[l][t[len]], st2[r - p[len] + ][t[len]]);
} void get()
{
int lim = n << ;
for(R k = ; k < lim; k ++)//枚举A的长度
{
int maxn = , maxn2 = ;
for(R i = ; i <= n; i += k)
{
int j = i + k;//j为下一段开头
if(j > n) break;
if(i > maxn)
{
int lcp = get1(i, j), lcs = get2(i, j);
maxn = i + lcp - ;
int l = i - lcs + , r = j + lcp - * k;
if(lcp + lcs > k) ++ f[l], -- f[r + ];
}
if(i > maxn2)
{
int lcp = get2(n - i + , n - j + ), lcs = get1(n - i + , n - j + );
maxn2 = i + lcp - ;
int l = i - lcs + , r = j + lcp - * k;
if(lcp + lcs > k) ++ g[l], -- g[r + ];
}
}
}
for(R i = ; i <= n; i ++) f[i] += f[i - ], g[i] += g[i - ];
rev(g + , g + n + );
} void work()
{
ans = ;//f是开头
for(R i = ; i <= n; i += )
ans += 1LL * f[i] * g[i - ] + ((i + > n) ? : 1LL * f[i + ] * g[i]);
printf("%lld\n", ans);
} int main()
{
// freopen("in.in", "r", stdin);
scanf("%d", &T);
while(T --)
{
init();
pre();
get_sa();
build();
build1();//建st(维护LCP)
rev(s + , s + n + );//翻转
// memset(rk, 0, sizeof(rk));//因为对于单组数据而言,长度不变,所以rk不必再次清空
get_sa();
build();
build2();//建st2(维护LCS)
get();
work();
}
// fclose(stdin);
return ;
}
[NOI2016]优秀的拆分 后缀数组的更多相关文章
- 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]至少两个点. 然后我们可以 ...
- [NOI2016]优秀的拆分(SA数组)
[NOI2016]优秀的拆分 题目描述 如果一个字符串可以被拆分为 \(AABB\) 的形式,其中 A和 B是任意非空字符串,则我们称该字符串的这种拆分是优秀的. 例如,对于字符串 \(aabaaba ...
- UOJ#219. 【NOI2016】优秀的拆分 [后缀数组 ST表]
#219. [NOI2016]优秀的拆分 题意:求有多少AABB样子的子串,拆分不同的同一个子串算多个 一开始一直想直接求,并不方便 然后看了一眼Claris的题解的第一行就有思路了 如果分开,求\( ...
- NOI 2016 优秀的拆分 (后缀数组+差分)
题目大意:给你一个字符串,求所有子串的所有优秀拆分总和,优秀的拆分被定义为一个字符串可以被拆分成4个子串,形如$AABB$,其中$AA$相同,$BB$相同,$AB$也可以相同 作为一道国赛题,95分竟 ...
- luogu1117 优秀的拆分 (后缀数组)
考虑分别计算每个位置作为AA的末尾或者BB的开头的个数 最后乘一乘就是答案 据说是套路的计算AA的方法: 首先枚举A的长度L,然后每L个字符当做一个关键点,这样的话,一个AA包含且只包含相邻两个关键点 ...
- [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 ...
随机推荐
- DSP5509项目之用FFT识别钢琴音调(4)之麦克风输入和Line in输入
1. 麦克风输入需要修改的内容,之前的版本是LINE IN的输入.实现功能,检测麦克风的输入,并且同时在耳机里面播放. #include <csl.h> #include <csl_ ...
- Python接口测试实战3(上)- Python操作数据库
如有任何学习问题,可以添加作者微信:lockingfree 课程目录 Python接口测试实战1(上)- 接口测试理论 Python接口测试实战1(下)- 接口测试工具的使用 Python接口测试实战 ...
- gtest命令行测试案例
使用gtest编写的测试案例通常本身就是一个可执行文件,因此运行起来非常方便.同时,gtest也为我们提供了一系列的运行参数(环境变量.命令行参数或代码里指定),使得我们可以对案例的执行进行一些有效的 ...
- MapPartition和Map的区别
在Spark中有map和mapPartitions算子,处理数据上,有一些区别 主要区别: map是对rdd中的每一个元素进行操作: mapPartitions则是对rdd中的每个分区的迭代器进行操作 ...
- 【MySQL解惑笔记】Mysql5.7.x无法开启二进制日志
一.开启二进制日志 1)未开启二进制日志之前: mysql> show variables like 'log_bin'; +---------------+-------+ | Variabl ...
- R-CNN学习总结
R-CNN是一个比较早期的用于目标检测方法,但却十分经典,在此结合论文对这一方法做一个总结. (写给小白:通过下图简单理解图像分类,图像定位,目标检测和实例分割) R-CNN方法提出的背景: 1.近1 ...
- Golang项目开发管理
工具 1. task(项目管理,类似于make) go get -u -v github.com/go-task/task/cmd/task 2. gopm(go依赖管理) go get -u git ...
- centos 系统初始化
centos 系统初始化 #!/bin/bash # author cfwl create date of 2012-10-21 # blog http://cfwlxf.blog.51cto.com ...
- tcp三次握手 四次挥手 (转)
转自: http://blog.csdn.net/whuslei/article/details/6667471 建立TCP需要三次握手才能建立,而断开连接则需要四次握手.整个过程如下图所示: 先来看 ...
- PIGCMS 关闭聊天机器人(小黄鸡)
无脑操作举例 1.找到 WeixinAction.class.php 文件,路径: 你的版本\PigCms\Lib\Action\Home 2.查询 function chat ,在 chat() 函 ...