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

  

posted @ 2016-06-18 21:32 _Horizon 阅读(...) 评论(...) 编辑 收藏