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...)题解: 后缀自动机,线段树 先对原串建立后缀自动机,不难发现, 会影响答案是 ...
随机推荐
- 关于final关键字和抽象类,接口
final主要用于修饰类,方法以及属性 当用于修饰类时,则这个类不能被子类继承,换而言之该类没有子类 当用于修饰方法时,该方法也是不能被复写的 当用于修饰属性时,同理属性值不能被更改 抽象类,当存在抽 ...
- 敏捷遇上UML——软创基地马年大会(深圳站 2014-3-15)
邀请函: 尊敬的阁下: 我们将在深圳为您奉献高端知识大餐,当敏捷遇上UML,会发生怎样的化学作用呢?首席专家张老师将会为您分享需求分析及软件设计方面的最佳实践,帮助您掌握敏捷.UML及两者相结合的实战 ...
- 如何查看Windows服务器运行了多长时间
前言:有时候管理.维护Windows服务器需要定期重启服务器(为什么需要重启,你懂的),但是这个"定期"有时候会受很多因素影响,例如某台服务器忘了重启:某台服务器那个时间段业务繁忙 ...
- sql优化阶段性总结以及反思
Sql优化思路阶段性心得: 这段时间的优化做了好几个案例,其实有很多的类似点,都是好几张大表的相互连接,然后执行长达好几个小时,甚至都跑不出来. 自己差不多的思路就是Parallel full tab ...
- 实时事件统计项目:优化solr和morphline的时间字段
morphline优化,如下: 传过来的时间戳被复制到3个字段:eventTimeInMinuteChina_tdt ,eventTimeInMinuteUTC_tdt ,eventTimeInHou ...
- java数组对象的浅层复制与深层复制
实际上,java中数组对象的浅层复制只是复制了对象的引用(参考),而深层复制的才是对象所代表的值.
- service mysql start出错,
service mysql start出错,mysql启动不了,解决mysql: unrecognized service错误的方法如下: [root@ctohome.com ~]# service ...
- Nginx服务状态监控
在Nginx的插件模块中有一个模块stub_status可以监控Nginx的一些状态信息,默认安装可能没有这个模块,手动编译的时候加一下即可. 1. 模块安装 先使用命令查看是否已经安装这个模块: [ ...
- delphi WebBrowser控件上网页验证码图片识别教程(一)
步骤一:获取网页中验证码图片的url地址 在delphi中加入一个BitBtn和一个memo以及WebBrowser控件实现网页中验证码图片的url地址的获取 程序如下:procedure TForm ...
- Java基础知识笔记(七:接口、变量作用域和参数传递)
一.接口 Java语言不允许一个子类拥有多个直接父类,即任何子类只能有一个直接父类.但允许一个类实现多个接口,即在定义类的接口名称列表中可以包含1个或多个接口名称,从而实现多重继承的特性.接口的定义格 ...