BZOJ 3230: 相似子串
3230: 相似子串
Time Limit: 20 Sec Memory Limit: 128 MB
Submit: 1485 Solved: 361
[Submit][Status][Discuss]
Description
Input
输入第1行,包含3个整数N,Q。Q代表询问组数。
第2行是字符串S。
接下来Q行,每行两个整数i和j。(1≤i≤j)。
Output
输出共Q行,每行一个数表示每组询问的答案。如果不存在第i个子串或第j个子串,则输出-1。
Sample Input
ababa
3 5
5 9
8 10
Sample Output
16
-1
HINT
样例解释
第1组询问:两个子串是“aba”,“ababa”。f = 32 + 32 = 18。
第2组询问:两个子串是“ababa”,“baba”。f = 02 + 42 = 16。
第3组询问:不存在第10个子串。输出-1。
数据范围
N≤100000,Q≤100000,字符串只由小写字母'a'~'z'组成
Source
Source里说的十分清楚了,题目本身也很水。
求出后缀数组,再把字符串reverse后求出“前缀数组”。
通过后缀数组可以对子串按排名进行定位,然后查询正反LCP即可。
#include <bits/stdc++.h> template <class T>
T sqr(T x)
{
return x*x;
} typedef long long longint; const int maxn = ;
const longint inf = 1e9; int n, m;
char s[maxn];
longint g[maxn];
longint pre[maxn]; class SuffixArray
{
public:
int sa[maxn], rk[maxn], ht[maxn]; inline void init(void)
{
memset(ca, , sizeof(ca)); for (int i = ; i <= n; ++i)
++ca[s[i]]; for (int i = ; i <= ; ++i)
ca[i] += ca[i - ]; for (int i = n; i >= ; --i)
sa[ca[s[i]]--] = i; rk[sa[]] = ; for (int i = ; i <= n; ++i)
rk[sa[i]] = rk[sa[i - ]] + (s[sa[i]] != s[sa[i - ]]); for (int l = ; rk[sa[n]] < n; l <<= )
{
memset(ca, , sizeof(ca));
memset(cb, , sizeof(cb)); for (int i = ; i <= n; ++i)
{
++ca[wa[i] = rk[i]];
++cb[wb[i] = i + l <= n ? rk[i + l] :];
} for (int i = ; i <= n; ++i)
{
ca[i] += ca[i - ];
cb[i] += cb[i - ];
} for (int i = n; i >= ; --i)
ta[cb[wb[i]]--] = i; for (int i = n; i >= ; --i)
sa[ca[wa[ta[i]]]--] = ta[i]; rk[sa[]] = ; for (int i = ; i <= n; ++i)
rk[sa[i]] = rk[sa[i - ]] + (wa[sa[i]] != wa[sa[i - ]] || wb[sa[i]] != wb[sa[i - ]]);
} for (int i = , j = ; i <= n; ++i)
{
if (--j < )j = ;
while (s[i + j] == s[sa[rk[i] - ] + j])++j;
ht[rk[i]] = j;
} build(, , n);
} inline int lcp(int a, int b)
{
a = rk[a];
b = rk[b]; if (a > b)
{
a ^= b;
b ^= a;
a ^= b;
} return query(, , n, a + , b);
}
private:
void build(int t, int l, int r)
{
if (l == r)
tr[t] = ht[l];
else
{
int mid = (l + r) >> ;
build(t << , l, mid);
build(t << | , mid + , r);
tr[t] = std::min(tr[t << ], tr[t << | ]);
}
} int query(int t, int l, int r, int a, int b)
{
if (l == a && r == b)
return tr[t];
else
{
int mid = (l + r) >> ;
if (b <= mid)
return query(t << , l, mid, a, b);
else if (a > mid)
return query(t << | , mid + , r, a, b);
else
return std::min(query(t << , l, mid, a, mid), query(t << | , mid + , r, mid + , b));
}
} int ta[maxn], wa[maxn], wb[maxn], ca[maxn], cb[maxn], tr[maxn << ];
}A, B; signed main(void)
{
scanf("%d%d%s", &n, &m, s + ); g[] = -; for (int i = ; i <= n; ++i)
g[i] = g[i >> ] + ; A.init();
std::reverse(s + , s + +n);
B.init(); pre[] = ; for (int i = ; i <= n; ++i)
pre[i] = pre[i - ] + n - A.sa[i] + - A.ht[i]; for (int i = ; i <= m; ++i)
{
longint lt, rt; scanf("%lld%lld", <, &rt); if (lt > pre[n] || rt > pre[n])
{ puts("-1"); continue; } int id1, id2, a1, b1, a2, b2; id1 = std::lower_bound(pre + , pre + + n, lt) - pre;
id2 = std::lower_bound(pre + , pre + + n, rt) - pre; a1 = A.sa[id1];
a2 = A.sa[id2]; b1 = a1 + A.ht[id1] - + lt - pre[id1 - ];
b2 = a2 + A.ht[id2] - + rt - pre[id2 - ]; longint ans = , tmp; tmp = a1 == a2 ? inf : A.lcp(a1, a2);
tmp = std::min(tmp, (longint)std::min(b1 - a1 + , b2 - a2 + )); ans += sqr(tmp); tmp = b1 == b2 ? inf : B.lcp(n - b1 + , n - b2 + );
tmp = std::min(tmp, (longint)std::min(b1 - a1 + , b2 - a2 + )); ans += sqr(tmp); printf("%lld\n", ans);
}
}
@Author: YouSiki
BZOJ 3230: 相似子串的更多相关文章
- BZOJ 3230 相似子串 | 后缀数组 二分 ST表
BZOJ 3230 相似子串 题面 题解 首先我们要知道询问的两个子串的位置. 先正常跑一遍后缀数组并求出height数组. 对于每一个后缀suffix(i),考虑以i开头的子串有多少是之前没有出现过 ...
- BZOJ 3230: 相似子串( RMQ + 后缀数组 + 二分 )
二分查找求出k大串, 然后正反做后缀数组, RMQ求LCP, 时间复杂度O(NlogN+logN) -------------------------------------------------- ...
- bzoj 3230 相似子串——后缀数组
题目:https://www.lydsy.com/JudgeOnline/problem.php?id=3230 作出后缀数组,从 LCP 看每个位置对于本质不同子串的贡献,而且他们已经按前面部分排好 ...
- bzoj 3230 相似子串 —— 后缀数组+二分
题目:https://www.lydsy.com/JudgeOnline/problem.php?id=3230 先算出每个后缀贡献子串的区间: 然后前缀LCP直接查询,后缀LCP二分长度,查询即可: ...
- bzoj 3230: 相似子串【SA+st表+二分】
总是犯低级错误,st表都能写错-- 正反分别做一遍SA,预处理st表方便查询lcp,然后处理a[i]表示前i个后缀一共有多少个本质不同的子串,这里的子串是按字典序的,所以询问的时候直接在a上二分排名就 ...
- BZOJ 3230: 相似子串(后缀数组)
传送门 解题思路 其实题目挺好想的.首先子串排名可以由后缀数组求得,因为不算重复的,所以后缀数组的每个后缀排名的去掉\(lcp\)的前缀排名为当前后缀的子串排名.这样就可以预处理出每个后缀的\(l,r ...
- BZOJ 3230 相似子串 ——后缀数组
题目的Source好有趣. 我们求出SA,然后求出每一个后缀中与前面本质不同的字符串的个数. 然后二分求出当前的字符串. 然后就是正反两次后缀数组求LCP的裸题了. 要注意,这时两个串的起点可能会相同 ...
- BZOJ 1396: 识别子串( 后缀数组 + 线段树 )
这道题各位大神好像都是用后缀自动机做的?.....蒟蒻就秀秀智商写一写后缀数组解法..... 求出Height数组后, 我们枚举每一位当做子串的开头. 如上图(x, y是height值), Heigh ...
- ●BZOJ 1396 识别子串
题链: http://www.joyoi.cn/problem/tyvj-2301(非权限OI患者,苟且在joyoi...)题解: 后缀自动机,线段树 先对原串建立后缀自动机,不难发现, 会影响答案是 ...
随机推荐
- iOS Swift-HelloWord
iOS Swift-HelloWord 按部就班选择Swif开发语言,输出HelloWord. override func viewDidLoad() { super.viewDidLoad() pr ...
- wireshark 实用过滤表达式(针对ip、协议、端口、长度和内容)
首先说几个最常用的关键字,"eq" 和 "=="等同,可以使用 "and" 表示并且,"or"表示或者."!& ...
- CLR垃圾回收的设计
作者: Maoni Stephens (@maoni0) - 2015 附: 关于垃圾回收的信息,可以参照本文末尾资源章节里引用的垃圾回收手册一书. 组件架构 GC包含的两个组件分别是内存分配器和垃圾 ...
- Linux平台卸载MySQL总结
如何在Linux下卸载MySQL数据库呢? 下面总结.整理了一下Linux平台下卸载MySQL的方法. MySQL的安装主要有三种方式:二进制包安装(Using Generic Binaries).R ...
- W3School-CSS 表格实例
CSS 表格实例 CSS 实例 CSS 背景实例 CSS 文本实例 CSS 字体(font)实例 CSS 边框(border)实例 CSS 外边距 (margin) 实例 CSS 内边距 (paddi ...
- 使用 expect 命令执行自动分发系统
一.命令 except 实例详解 1. 介绍 expect 使用场景 expect可以让我们实现自动登录远程机器,并且可以实现自动远程执行命令.当然若是使用不带密码的密钥验证同样可以实现自动登录和自动 ...
- ANDROID开发中注意不同手机CPU架构对SO文件的不同需求。
如果没有对应于手机的SO文件,那么在调用第三方SDK时,会经常发生莫明其妙的错误.所以了解你调式或开发的目的手机CPU架构是很有必要的.
- Linux初学:(二)Shell环境与命令基础
博客园(FOREVER_ENJOY):http://www.cnblogs.com/zyx1314/ 本文版权归作者所有:欢迎转载!请注明文章作者和原文连接 Shell是什么? 1. Shell作为应 ...
- Eclipse导入MyEclipse创建的web项目报错的解决方法
将myeclipse中开发的动态web项目直接引入到eclipse中继续开发,Eclipse中会报项目有错,如下图
- 【2016-10-24】【坚持学习】【Day11】【WPF】【MVVM】
今天学习wpf的mvvm 人家说,APS.NET ===>MVC WPF===>MVVM 用WPF不用mvvm的话,不如用winform... 哈哈,题外话. 定义: MVVM: WPF的 ...