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...)题解: 后缀自动机,线段树 先对原串建立后缀自动机,不难发现, 会影响答案是 ...
随机推荐
- numpy 函数一:linspace
接触 numpy 遇到的第一个函数可能就是 linspace 函数,但是对于我们这种没有学过 matlab 的人来说,根本不知道这是什么. 所以只能自己查资料. 词典显示: 线性等分向量 线性平分矢量 ...
- GRANT/SELECT View时的遭遇ORA-01720和ORA-01031错误
关于创建视图遇到ORA-01031错误信息,请参考我以前整理的一篇文章Create view failed with ORA-01031:insufficient privileges,本来以为我那篇 ...
- Linux 如何通过命令仅获取IP地址
一同事的朋友正在参加笔试,遇到这么一个问题让他帮忙解决,结果同事又找到我帮他搞定.真是感慨:通讯发达在某些方面来说,真不知是不是好事啊!题目大致如下所示,一般我们使用ifconfig查看网卡信息,请问 ...
- redis、memcache、mongoDB 做了对比
from: http://yang.u85.us/memcache_redis_mongodb.pdf 从以下几个维度,对redis.memcache.mongoDB 做了对比. 1.性能 都比较 ...
- Android原生游戏开发:使用JustWeEngine开发微信打飞机
使用JustWeEngine开发微信打飞机: 作者博客: 博客园 引擎地址:JustWeEngine 示例代码:EngineDemo JustWeEngine? JustWeEngine是托管在Git ...
- QuickHit项目(输出字符串游戏)
public class leve { private int leveNo; private int strLength; private int strTimes; private int tim ...
- dotnet core 出现Can not find runtime target for framework '.NETCoreApp,Version=v1.6' 的解决办法
如果你在更新dotnet core新的类库后运行程序提示如下的错误: Can not find runtime target for framework '.NETCoreAPP, Version=v ...
- 前端之html
前端之html 本节内容 前端概述 html结构 标签探秘 <!DOCTYPE html>标签 head标签 body标签 1.前端概述 一个web服务的组成分为前端和后端部分,前端部分负 ...
- DbContext 和ObjectContext两者的区别
http://blog.csdn.net/lvjin110/article/details/24642911 ObjectContext是一种模型优先的开发模式,DbContext是代码优先的开发模式 ...
- ListIterator接口,双向输出。
1,ListIterator接口与Itreator接口的区别. 2,使用限制. 定义如下: public interface ListIterator<E>extends Iterator ...