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...)题解: 后缀自动机,线段树 先对原串建立后缀自动机,不难发现, 会影响答案是 ...
随机推荐
- ASP.NET MVC 的 WebGrid 的 6 个重要技巧 【已翻译100%】
ASP.NET MVC 中 WebGrid 的 6 个重要技巧 https://www.oschina.net/translate/webgrid-in-asp-net-mvc-important-t ...
- C语言链表实现约瑟夫环问题
需求表达:略 分析: 实现: #include<stdio.h> #include<stdlib.h> typedef struct node { int payload ; ...
- Reporting Service 告警"w WARN: Thread pool pressure. Using current thread for a work item"
如果Reporting Service偶尔出现不可访问或访问出错情况,这种情况一般没有做监控的话,很难捕捉到.出现这种问题,最好检查Reporting Service的日志文件. 今天早上就遇到这样一 ...
- MS SQL 监控错误日志的告警信息
SQL Server的错误消息(Error Message)按照消息的严重级别一共划分25个等级,级别越高,表示严重性也越高.但是如果你统计sys.messages,你会发现,实际上只有16(SQL ...
- Win7 安装SQL SERVER 2012需要SP1补丁
在操作系统Win7上安装SQL Server 2012时,报如下错误: 也就是说SQL Server 2012如要要安装在Windows 7 上,则至少需要安装SP1补丁.否则就会弹出上面提示信息.关 ...
- MongoDB学习笔记~ObjectId主键的设计
回到目录 说一些关于ObjectId的事 MongoDB确实是最像关系型数据库的NoSQL,这在它主键设计上可以体现的出来,它并没有采用自动增长主键,因为在分布式服务器之间做数据同步很麻烦,而是采用了 ...
- InfluxDB学习之InfluxDB数据保留策略(Retention Policies)
InfluxDB每秒可以处理成千上万条数据,要将这些数据全部保存下来会占用大量的存储空间,有时我们可能并不需要将所有历史数据进行存储,因此,InfluxDB推出了数据保留策略(Retention Po ...
- html中的meta详解
1 name=viewport <meta name="viewport" content="width=device-width,initial-scale=1 ...
- windows系统命令总结
windows系统命令总结 IIS管理器:inetmgr SQL server数据库管理器:ssms windows服务:services.msc
- EF6 如何判断DataContext有修改,以及如何放弃修改
如何判断DataContext有修改: EF6的 using (var db = new Model1()) { if (db.ChangeTracker.HasChanges()) { Cons ...