[BZOJ 3145][Feyat cup 1.5]Str 解题报告
[Feyat cup 1.5]Str
Description
Arcueid,白姬,真祖的公主。在和推倒贵看电影时突然对一个问题产生了兴趣:
我们都知道真祖和死徒是有类似的地方。那么从现代科学的角度如何解释
呢?自然就得研究遗传密码了。Arcueid得知了两者的DNA片段,想寻求一个
DNA片段,使得其在两者的DNA中都出现过。
我们知道公主的脑袋有点不太灵活,如果两个DNA片段只有一个位置不
同,她也会将其认为是相同的。所以请您找出这样的最长的DNA片段吧。
Input
两行,每行一个字符串。
Output
一个整数,表示最长的 DNA 片段的长度。
Sample Input
aabbe
acbbc
Sample Output
4
HINT
100% 的数据 n<=10^5;m<=10^5 。
Sol:
2015年集训队论文里有讲到这个题目
Sam + Sa
将a,b拼接起来,中间加一个特殊字符
考虑后缀自动机上的一个节点,对应A,B串的Right集合,已知这些点的最长公共后缀为这个节点的len,暴力的做法可以枚举两个集合的点对(a,b),对于a+2,b+2求lcp更新
但是复杂度很高,考虑将A,B集合中(a+2)(b+2)这些点按照后缀排序,要求的只有相邻的所属的集合不同的两个后缀的lcp来更新答案
由于空间的限制,要在parent树自底向上维护set,启发式合并维护信息
要注意的地方是跨越两个串的地方不能直接丢进set里,会影响rank的比较以及ans的更新
所以记录一些奇奇怪怪的东西。。这个节点在A,B串后缀出现的位置的最大,最小值(语死早说不清楚)
代码异常丑陋,还是不要看的好。。
#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <vector>
#include <map>
#include <set> #define maxn 500010 using namespace std; int mark[maxn];
struct Node{int len, link; map<int, int> nxt;}st[maxn];
int root, size, last;
void init(){
root = size = last = 0;
st[root].len = 0;
st[root].link = -1;
}
void Extend(int c){
int p = last, cur = ++ size;
st[cur].len = st[p].len + 1;
for(; ~p && st[p].nxt[c] == 0; p = st[p].link)
st[p].nxt[c] = cur;
if(p == -1)
st[cur].link = root;
else{
int q = st[p].nxt[c];
if(st[q].len == st[p].len + 1)
st[cur].link = q;
else{
int clone = ++ size;
st[clone] = st[q];
st[clone].len = st[p].len + 1;
for(; ~p && st[p].nxt[c] == q; p = st[p].link)
st[p].nxt[c] = clone;
st[q].link = st[cur].link = clone;
}
}
last = cur;
} int n, m, N, checker;
char a[maxn], b[maxn];
int str[maxn]; int sa[maxn], t1[maxn], t2[maxn], c[maxn], rk[maxn], ht[maxn], ST[maxn][20], lg[maxn], stsize;
int lcp(int x, int y){
if(x > N || y > N)return 0;
x = rk[x], y = rk[y];
if(x > y)swap(x, y);
y --; int k = lg[y-x+1];
return min(ST[x][k], ST[y-(1<<k)+1][k]);
} /*
void Get_sa(int n, int m) {
int *x = wa, *y = wb, *t, i, j, p;
for(i = 0 ; i < m ; ++ i) ws[i] = 0;
for(i = 0 ; i < n ; ++ i) ++ ws[x[i] = w[i]];
for(i = 1 ; i < m ; ++ i) ws[i] += ws[i-1];
for(i = n-1 ; i >= 0 ; -- i) sa[-- ws[x[i]]] = i;
for(j = 1, p = 1 ; p < n ; j <<= 1, m = p) {
// printf("j = %d, p = %d \n", j, p);
for(i = n-j, p = -1 ; i < n ; ++ i) y[++ p] = i;//puts("NO");
for(i = 0 ; i < n ; ++ i) if(sa[i]>=j) y[++ p] = sa[i]-j;
for(i = 0 ; i < n ; ++ i) wv[i] = x[y[i]];
for(i = 0 ; i < m ; ++ i) ws[i] = 0;
for(i = 0 ; i < n ; ++ i) ++ ws[wv[i]];
for(i = 1 ; i < m ; ++ i) ws[i] += ws[i-1];
for(i = n-1 ; i >= 0 ; -- i) sa[-- ws[wv[i]]] = y[i];
// puts("Oh NO");
for(t = x, x = y, y = t, i = 1, x[sa[0]] = 0, p = 1 ; i < n ; ++ i) {
x[sa[i]] = cmp(t, sa[i-1], sa[i], j)?p-1:p ++;
}
}
return;
}
*/ void getsa(int n, int m){
int *x = t1, *y = t2;
for(int i = 0; i < m; i ++)c[i] = 0;
for(int i = 0; i < n; i ++)c[x[i] = str[i]] ++;
for(int i = 1; i < m; i ++)c[i] += c[i-1];
for(int i = n-1; ~ i; i --)sa[-- c[x[i]]] = i; for(int k = 1; k <= n; k <<= 1){
int p = 0;
for(int i = n-k; i < n; i ++)y[p ++] = i;
for(int i = 0; i < n; i ++)if(sa[i] >= k)y[p ++] = sa[i] - k; for(int i = 0; i < m; i ++)c[i] = 0;
for(int i = 0; i < n; i ++)c[x[y[i]]] ++;
for(int i = 1; i < m; i ++)c[i] += c[i-1];
for(int i = n-1; ~ i; i --)sa[-- c[x[y[i]]]] = y[i]; swap(x, y); x[sa[0]] = 0, p = 1;
for(int i = 1; i < n; i ++)
x[sa[i]] = y[sa[i]] == y[sa[i-1]] && y[sa[i]+k] == y[sa[i-1]+k] ? p-1 : p++;
if(p >= n)break;
m = p;
} int k = 0;
for(int i = 0; i < n; i ++)rk[sa[i]] = i; for(int i = 0; i < n; i ++){
if(rk[i] == 0){ht[0] = 0; continue;}
if(k) k --; int j = sa[rk[i]-1];
while(str[i+k] == str[j+k]) k ++;
ht[rk[i]] = k;
}
for(int i = n; i; i --)sa[i] = sa[i-1] + 1;
for(int i = 1; i <= n; i ++)rk[sa[i]] = i;
for(int i = 1; i < n; i ++)ST[i][0] = ht[i];
stsize = n - 1, lg[0] = -1;
for(int i = 1; i < n; i ++)lg[i] = lg[i>>1] + 1;
for(int j = 1; 1<<j <= stsize; j ++)
for(int i = 1; i+(1<<j)-1 <= stsize; i ++)
ST[i][j] = min(ST[i][j-1], ST[i+(1<<j-1)][j-1]);
} int h[maxn], cnt, ans, Nw, fg;
struct Edge{int to, nxt;} edge[maxn];
void addedge(int u, int v){
edge[++ cnt] = (Edge){v, h[u]}; h[u] = cnt;
} struct cmp{
bool operator ()(const int& x, const int& y){
return rk[x] < rk[y];
}
}; typedef set<int, cmp> se;
typedef set<int, cmp>::iterator iter;
vector<int> V[maxn];
se s[maxn]; inline void cmax(int x, int y){
if(x > y)swap(x, y);
if(y <= n || x > n)return;
if(x <= n ^ y <= n)
ans = max(ans, Nw + 1 + lcp(x, y));
} bool ha[maxn], hb[maxn], fg1, FG1, fg2, FG2, FG3, FG4; int mx1[maxn], mx2[maxn]; void merge(se& x, se& y, int u, int v){
if(x.size() < y.size())swap(x, y), swap(V[u], V[v]);
for(iter it = y.begin(); it != y.end(); it ++){
iter p = x.upper_bound(*it);
if(p != x.end())cmax(*p, *it);
if(p != x.begin())p --, cmax(*p, *it);
} FG1 = fg1 = ha[v], FG2 = fg2 = hb[v], FG3 = mx1[v] - Nw >= 1, FG4 = mx2[v] - Nw >= n + 2;
for(int i = 0; i < V[v].size(); i ++){
int nw = V[v][i];
fg1 |= nw <= n, FG1 |= nw < n;
fg2 |= nw > n && nw <= checker, FG2 |= nw > n && nw < checker;
if(nw <= n)FG3 |= nw - Nw >= 1; else FG4 |= nw - Nw >= n + 2;
} for(int i = 0; i < V[u].size(); i ++){
int w = V[u][i];
if(w <= n && fg2){
ans = max(ans, Nw + (FG2 && ((w == n - 1 || w == n + m))));
ans = max(ans, Nw + (FG4 && w - Nw != 0 && w - Nw != n + 1));
}
if(w > n && fg1){
ans = max(ans, Nw + (FG1 && (w == n - 1 || w == n + m)));
ans = max(ans, Nw + (FG3 && w - Nw != 0 && w - Nw != n + 1));
}
} FG1 = fg1 = ha[u], FG2 = fg2 = hb[u], FG3 = mx1[u] - Nw >= 1, FG4 = mx2[u] - Nw >= n + 2;
for(int i = 0; i < V[u].size(); i ++){
int nw = V[u][i];
fg1 |= nw <= n, FG1 |= nw < n;
fg2 |= nw > n && nw <= checker, FG2 |= nw > n && nw < checker;
if(nw <= n)FG3 |= nw - Nw >= 1; else FG4 |= nw - Nw >= n + 2;
} for(int i = 0; i < V[v].size(); i ++){
int w = V[v][i];
if(w <= n && fg2){
ans = max(ans, Nw + (FG2 && ((w == n - 1 || w == n + m))));
ans = max(ans, Nw + (FG4 && w - Nw != 0 && w - Nw != n + 1));
}
if(w > n && fg1){
ans = max(ans, Nw + (FG1 && (w == n - 1 || w == n + m)));
ans = max(ans, Nw + (FG3 && w - Nw != 0 && w - Nw != n + 1));
}
} for(int i = 0; i < V[v].size(); i ++)
V[u].push_back(V[v][i]); for(iter it = y.begin(); it != y.end(); it ++)
x.insert(*it);
V[v].clear(), y.clear();
} void dfs(int u){
if(~mark[u]){
if((mark[u] > n+1 && mark[u]+2 <= checker) || mark[u]+2 <= n){
s[u].insert(mark[u]+2);
ha[u] |= mark[u] <= n;
hb[u] |= mark[u] > n;
}
else V[u].push_back(mark[u]);
if(mark[u] <= n)mx1[u] = max(mx1[u], mark[u]);
else mx2[u] = max(mx2[u], mark[u]);
}
for(int i = h[u]; i; i = edge[i].nxt){
int v = edge[i].to;
dfs(v);
Nw = st[u].len;
merge(s[u], s[v], u, v);
ha[u] |= ha[v];
hb[u] |= hb[v];
mx1[u] = max(mx1[u], mx1[v]);
mx2[u] = max(mx2[u], mx2[v]);
}
} int nd[10]; int main(){
init();ans = 1;
scanf("%s%s", a + 1, b + 1);
n = strlen(a + 1), m = strlen(b + 1); for(int i = 1; i <= n; i ++)Extend(a[i] - 'a'); Extend(27);
for(int i = 1; i <= m; i ++)Extend(b[i] - 'a'); for(int i = 1; i <= size; i ++)addedge(st[i].link, i);
memset(mark, -1, sizeof mark); int cur = root; mark[root] = 0;
for(int i = 1; i <= n; i ++)cur = st[cur].nxt[a[i] - 'a'], mark[cur] = i;
cur = st[cur].nxt[27];
for(int i = 1; i <= m; i ++)cur = st[cur].nxt[b[i] - 'a'], mark[cur] = i+n+1; N = 0, checker = n + m + 1;
for(int i = 1; i <= n; i ++)str[N ++] = a[i] - 'a' + 1; str[N ++] = 27;
for(int i = 1; i <= m; i ++)str[N ++] = b[i] - 'a' + 1;
getsa(n+m+2, 30); dfs(0);
printf("%d\n", ans);
return 0;
}
[BZOJ 3145][Feyat cup 1.5]Str 解题报告的更多相关文章
- Bzoj 3145 - [Feyat cup 1.5]Str
bzoj 3145 - [Feyat cup 1.5]Str Description 给你两个长度\(10^5\)级别的串\(S, T\) 求\(S,T\)的最长模糊匹配公共子串 模糊匹配 : 至多一 ...
- BZOJ 1051 最受欢迎的牛 解题报告
题目直接摆在这里! 1051: [HAOI2006]受欢迎的牛 Time Limit: 10 Sec Memory Limit: 162 MBSubmit: 4438 Solved: 2353[S ...
- BZOJ3145 [Feyat cup 1.5]Str 后缀树、启发式合并
传送门--BZOJCH 考虑两种情况: 1.答案由一个最长公共子串+可能的一个模糊匹配位置组成.这个用SAM求一下最长公共子串,但是需要注意只出现在\(S\)的开头和\(T\)的结尾的子串是不能够通过 ...
- BZOJ3145 : [Feyat cup 1.5]Str
如果不存在模糊点,那么答案就是两个串的最长公共子串. 如果模糊点是某个串的开头或者结尾,那么可以暴力枚举另一个串中的某个前后缀更新答案. 否则,假设模糊点在第一个串里是$i$,在第二个串里是$j$,那 ...
- BZOJ 3809Gty的二逼妹子序列 解题报告+data marker
--BZOJ http://www.lydsy.com/JudgeOnline/problem.php?id=3809 考虑对l,r跑莫队,对一组维护美丽度出现次数的桶修改, 然后把桶序列用分块维护查 ...
- BZOJ 3173 [Tjoi2013] 最长上升子序列 解题报告
这个题感觉比较简单,但却比较容易想残.. 我不会用树状数组求这个原排列,于是我只好用线段树...毕竟 Gromah 果弱马. 我们可以直接依次求出原排列的元素,每次找到最小并且最靠右的那个元素,假设这 ...
- BZOJ 4027 [HEOI 2015] 兔子与樱花 解题报告
这个题看起来好神的感觉.实际上也好神... 我们可以考虑设 $f_u$ 表示以 $u$ 为根的子树中最多能删多少个点, 再设 $g_u$ 表示以 $u$ 为根的子树中删了 $f_u$ 个点之后,$u$ ...
- BZOJ 4864: [BeiJing 2017 Wc]神秘物质 解题报告
4864: [BeiJing 2017 Wc]神秘物质 Description 21ZZ 年,冬. 小诚退休以后, 不知为何重新燃起了对物理学的兴趣. 他从研究所借了些实验仪器,整天研究各种微观粒子. ...
- Facebook Hacker Cup 2014 Qualification Round 竞赛试题 Square Detector 解题报告
Facebook Hacker Cup 2014 Qualification Round比赛Square Detector题的解题报告.单击这里打开题目链接(国内访问需要那个,你懂的). 原题如下: ...
随机推荐
- PHP session的实现原理
PHP SESSION原理 我们知道,session是在服务器端保持用户会话数据的一种方法,对应的cookie是在客户端保持用户数据.HTTP协议是一种无状态协议,服务器响应完之后就失去了与浏览器的联 ...
- 【Other】千字文 硬笔 楷书 字帖
<千字文>是我国最优秀的一篇训蒙教材,用一千个汉字勾划出一部完整的中国文化史的基本轮廓,代表了中国传统教育启蒙阶段的最高水平.<千字文>通篇首尾连贯,音韵谐美,读起来朗朗上口, ...
- Datasets for Data Mining and Data Science
https://github.com/mattbane/RecommenderSystem http://grouplens.org/datasets/movielens/ KDDCUP-2012官网 ...
- table动态添加删除一行和改变标题
<style type="text/css"> body{ font-size:13px; line-height:25px; } table{ border-top: ...
- PHP 转换接口编码
2014年10月20日 10:45:19 有些时候调用接口的时候返回数据的编码不是utf-8的,需要转码 foreach ($arrInfo as $k => $v) { $encodeing ...
- JavaScript关闭窗口的同时打开新页面的方法
做网页的时候需要弹出一个小窗口,然后要实现一个功能就是鼠标点击超链接关闭小窗口并打开一个新页面,就如同下图: 这是一个小窗口,点击超链接这个窗口会关闭并且会正常在浏览器打开新页面,首先写js关闭窗口的 ...
- Extjs中给同一个GridPanel中的事件添加参数的方法
Extjs中给同一个GridPanel中的事件添加参数的方法: this.isUse = new Ext.Action({ text:'启用', scope ...
- iOS开发——开发实战篇&版本控制SVN和Git使用详解
版本控制SVN和Git使用详解 公司的实际开发中,在天朝使用较多的还是SVN,因为SVN是集中式的,在天朝上班你们都懂的! -----------------svn--------- ...
- 解决 g++ error:/usr/lib/rpm/redhat/redhat-hardened-cc1 No that file and directory
You need to install redhat-rpm-config which is required by some of the qt switches, probably: sudo d ...
- jquery学习笔记---插件开发模式和结构
JQuery插件开发http://www.cnblogs.com/damonlan/archive/2012/04/06/2434460.html github教程:https://github.co ...