[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; }
给时光以生命,而不是给生命以时光。