• @bzoj



    @description@

    给定 M 个 01 串表示文本库,再给 N 个询问。
    我们称一个子串是 “L - 熟悉” 的,当且仅当这个子串的长度大于等于 L 且是文本库中某一个串的子串。
    每次询问给出一个 01 串 A,如果可以把这个串分成若干段子串,其中 “L0 - 熟悉” 的子串长度和 >= 90%*|A|,则称 L0 满足要求。输出满足要求的 L0 最大值。

    input
    第一行两个整数 N,M。含义如上。
    接下来 M 行的 01 串表示文本库。
    接下来 N 行的 01 串表示询问。

    output
    对于每个询问,输出相应的答案。

    sample input
    1 2
    10110
    000001110
    1011001100
    sample output
    4
    sample explain
    |10110|0110|0|

    @solution@

    显然二分答案 + dp 判定。
    二分出 L,得到 dp[i] = max(dp[i-1], dp[j]+(i-j)),其中 j <= i-L 且 j+1...i 这一段是文本库某一个串的子串。
    这是一个很显然的单调队列可以优化的 dp。

    因此,我们需要求 A 的每一个前缀的某个长度最长的后缀,使这个后缀是文本串的某一个串的子串。
    假设文本库里面只有一个串,就会让人想起后缀自动机寻找两个串的公共子串的过程。也是对于每一个前缀求它长度最长的后缀。
    对于多个串,我们需要广义后缀自动机。

    广义后缀自动机,概念上就是对多个串建同一个后缀自动机,实现上就是如果要加入新的字符串,就将 last 指针重新移回 root。
    如果某个转移边以前出现过会怎样?你会发现新加入的点并不会与其他点连通,因为它不会和它之前的任何结点连边。

    @accepted code@

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    const int MAXN = 1100000;
    struct sam{
        sam *ch[2], *fa; int mx;
    }pl[2*MAXN + 5], *tcnt, *root, *lst;
    void init() {
        tcnt = root = lst = &pl[0];
    }
    sam *newnode() {
        return (++tcnt);
    }
    void sam_extend(int x) {
        sam *cur = newnode(), *p = lst;
        cur->mx = lst->mx + 1, lst = cur;
        while( p && !p->ch[x] )
            p->ch[x] = cur, p = p->fa;
        if( !p )
            cur->fa = root;
        else {
            sam *q = p->ch[x];
            if( q->mx == p->mx + 1 )
                cur->fa = q;
            else {
                sam *cne = newnode();
                (*cne) = (*q), cne->mx = p->mx + 1;
                q->fa = cur->fa = cne;
                while( p && p->ch[x] == q )
                    p->ch[x] = cne, p = p->fa;
            }
        }
    }
    char s[MAXN + 5];
    int f[MAXN + 5], dp[MAXN + 5], len;
    int que[MAXN + 5];
    bool check(int L) {
        for(int i=1;i<L;i++)
            dp[i] = 0;
        int s = 1, t = 0;
        for(int i=L;i<=len;i++) {
            while( s <= t && dp[i - L] - (i - L) > dp[que[t]] - que[t] )
                t--;
            que[++t] = i - L;
            while( s <= t && que[s] < i - f[i-1] )
                s++;
            dp[i] = dp[i-1];
            if( s <= t ) dp[i] = max(dp[i], dp[que[s]] + i - que[s]);
        }
        return 10*dp[len] >= 9*len;
    }
    int main() {
        init();
        int N, M; scanf("%d%d", &N, &M);
        for(int i=1;i<=M;i++) {
            scanf("%s", s); lst = root;
            int len = strlen(s);
            for(int j=0;j<len;j++)
                sam_extend(s[j] - '0');
        }
        for(int i=1;i<=N;i++) {
            scanf("%s", s); len = strlen(s);
            int le = 0, ri = len, res = 0; sam *nw = root;
            for(int j=0;j<len;j++) {
                while( nw && !nw->ch[s[j] - '0'] )
                    nw = nw->fa;
                if( !nw ) res = 0, nw = root;
                else res = min(res, nw->mx) + 1, nw = nw->ch[s[j] - '0'];
                f[j] = res;
            }
            while( le < ri ) {
                int mid = (le + ri + 1) >> 1;
                if( check(mid) ) le = mid;
                else ri = mid - 1;
            }
            printf("%d
    ", le);
        }
    }
    
    

    @details@

    本题好像听他们说,如果你乘 0.9 就会产生浮点误差,然后就会挂掉。
    有这么卡精度的吗?才 0.9 啊喂。

  • 相关阅读:
    P2216 [HAOI2007]理想的正方形(dp+单调队列优化)
    洛谷P1415 拆分数列(dp)
    2017 ACM-ICPC EC-Final ShangHai 东亚洲大陆-上海
    sql查询50题
    虚拟机安装
    Kick Start 2019 Round A Parcels
    Kick Start 2019 Round B Energy Stones
    【DP 好题】Kick Start 2019 Round C Catch Some
    【图论好题】ABC #142 Task F Pure
    【DP 好题】hihoCoder #1520 古老数字
  • 原文地址:https://www.cnblogs.com/Tiw-Air-OAO/p/10255035.html
Copyright © 2020-2023  润新知