[BZOJ 3145][Feyat cup 1.5]Str 解题报告

时间:2021-03-02 08:21:57

[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;
}