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

  

时间: 2024-12-30 03:32:55

[BZOJ 3145][Feyat cup 1.5]Str 解题报告的相关文章

BZOJ 1051 最受欢迎的牛 解题报告

题目直接摆在这里! 1051: [HAOI2006]受欢迎的牛 Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 4438  Solved: 2353[Submit][Status][Discuss] Description 每一头牛的愿望就是变成一头最受欢迎的牛.现在有N头牛,给你M对整数(A,B),表示牛A认为牛B受欢迎. 这 种关系是具有传递性的,如果A认为B受欢迎,B认为C受欢迎,那么牛A也认为牛C受欢迎.你的任务是求出有多少头 牛被所有的牛

hacker cup 2015 Round 1 解题报告

A:求区间内素因子个数等于n的数有多少个 解题思路:筛法 解题代码: 1 // File Name: a.cpp 2 // Author: darkdream 3 // Created Time: 2015年01月18日 星期日 13时54分20秒 4 5 #include<vector> 6 #include<list> 7 #include<map> 8 #include<set> 9 #include<deque> 10 #include&

BZOJ 3809Gty的二逼妹子序列 解题报告+data marker

--BZOJ http://www.lydsy.com/JudgeOnline/problem.php?id=3809 考虑对l,r跑莫队,对一组维护美丽度出现次数的桶修改, 然后把桶序列用分块维护查询 然后是吐槽: 内存28M,哦,这个题居然卡内存..... 卡内存!!! 然后我就为本校的权限号贡献了三次MLE...... 代码: 1 #include<cstdio> 2 #include<cmath> 3 #include<algorithm> 4 using st

BZOJ 3173 [Tjoi2013] 最长上升子序列 解题报告

这个题感觉比较简单,但却比较容易想残.. 我不会用树状数组求这个原排列,于是我只好用线段树...毕竟 Gromah 果弱马. 我们可以直接依次求出原排列的元素,每次找到最小并且最靠右的那个元素,假设这是第 $i$ 次找的,那么这就是原排列的第 $i$ 项,然后我们就把这个元素删去(变成很大的数),再把这个数以左的数都加 1,进行下一轮. 然后就是裸的最长上升子序列啦~~~ 时间复杂度 $O(n\log n)$,空间复杂度 $O(n)$. 1 #include <cstdio> 2 #inclu

Codeforces Round #472 (based on VK Cup 2018 Round 2)解题报告

A. Mystical Mosaic 题目大意: 给一个空白矩阵,每次可以选一些行和一些列并让他们的交点涂黑,每次选的行和列不能有交集. 给出最后的矩阵样子,问能不能经过若干次以上操作后得到最后的矩阵. 思路: 每一行都可以确定哪些列必须被覆盖记为Si,任意两个不同的行之间要么S相等要么相交为空集. 所以我们要做的就是确定任意两行,他们的S要么相等要么相交为空集,这是答案为Yes的充要条件. 代码: 1 #include <bits/stdc++.h> 2 using namespace st

Poi 2014 解题报告( 1 - 4 ,6 )

撸了一下Poi 2014 ,看了一下网上题解不多,所以决定写一下.有的题应该是数据不强水过去了,等北京回来在写一下复杂度比较靠谱的代码 o(╯□╰)o 第一题: 题意是给定一个长度不大于1000000,只包括p和j的串,求一个最长的子串,要求子串任何一个前缀和后缀都满足p的数量不少于j的数量. 首先把p当做1,把j当做0,算出前缀和 sum[] ,原来的问题就转化为求一个最长区间 [l,r] ,使得任意的i∈[l,r],都有 sum[i] - sum[l-1] >= 0 并且 sum[r] -

【百度之星2014~初赛(第二轮)解题报告】Chess

声明 笔者最近意外的发现 笔者的个人网站http://tiankonguse.com/ 的很多文章被其它网站转载,但是转载时未声明文章来源或参考自 http://tiankonguse.com/ 网站,因此,笔者添加此条声明. 郑重声明:这篇记录<[百度之星2014~初赛(第二轮)解题报告]Chess>转载自 http://tiankonguse.com/ 的这条记录:http://tiankonguse.com/record/record.php?id=667 前言 最近要毕业了,有半年没做

解题报告 之 POJ1226 Substrings

解题报告 之 POJ1226 Substrings Description You are given a number of case-sensitive strings of alphabetic characters, find the largest string X, such that either X, or its inverse can be found as a substring of any of the given strings. Input The first li

Winter-2-STL-E Andy&#39;s First Dictionary 解题报告及测试数据

use stringstream Time Limit:3000MS     Memory Limit:0KB Description Andy, 8, has a dream - he wants to produce his very own dictionary. This is not an easy task for him, as the number of words that he knows is, well, not quite enough. Instead of thin