【BZOJ2806】【CTSC2012】Cheat 广义后缀自动机+二分+Dp

题目

题目在这里

思路&做法

我们先对标准作文库建广义后缀自动机。

然后对于每一篇阿米巴的作文, 我们首先把放到广义后缀自动机跑一遍, 对于每一个位置, 记录公共子串的长度\((\)即代码和下文中的\(val\)数组\()\)

接着我们二分答案, 用DP检验。

Dp方程很好想, \(d_i = max \{ d_j + i - j \ | \ i-val_i <= j <= i-lim \}\)

可以用单点队列优化。

代码

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>

using namespace std;

const int N = 1300010; //数组不能太大, 会T的

int n, m;

int val[N];

struct Suffix_Automaton
{   int nxt[N][2], fail[N], sz;
    int len[N];
    int root;

    Suffix_Automaton() { }

    inline int newnode(int l)
    {   memset(nxt[sz], 0, sizeof(nxt[sz]));
        fail[sz] = 0;
        len[sz] = l;
        return sz++;
    }

    void init()
    {   sz = 1;
        root = newnode(0);
    }

    inline int idx(char x) { return x - ‘0‘; }

    int add(int last, char x)
    {   int c = idx(x);
        if (nxt[last][c])
        {   int p = last, q = nxt[last][c];
            if (len[q] == len[p] + 1)
                return q;
            else
            {   int u = newnode(len[p] + 1);
                for (int i = 0; i < 2; i++) nxt[u][i] = nxt[q][i];
                fail[u] = fail[q];
                fail[q] = u;
                while (p && nxt[p][c] == q)
                {   nxt[p][c] = u;
                    p = fail[p];
                }
                return u;
            }
        }
        else
        {   int now = newnode(len[last] + 1);
            int p = last;
            while (p && !nxt[p][c])
            {   nxt[p][c] = now;
                p = fail[p];
            }
            if (!p) fail[now] = root;
            else
            {   int q = nxt[p][c];
                if (len[q] == len[p] + 1)
                    fail[now] = q;
                else
                {   int u = newnode(len[p] + 1);
                    for (int i = 0; i < 2; i++) nxt[u][i] = nxt[q][i];
                    fail[u] = fail[q];
                    fail[now] = fail[q] = u;
                    while (p && nxt[p][c] == q)
                    {   nxt[p][c] = u;
                        p = fail[p];
                    }
                }
            }
            return now;
        }
    }

    void insert(char *s)
    {   int Len = strlen(s);
        int last = root;
        for (int i = 0; i < Len; i++)
            last = add(last, s[i]);
    }

    void work(char *str)
    {   int cnt = 0;
        int now = root;
        int Len = strlen(str+1);
        for (int i = 1; i <= Len; i++)
        {   int c = idx(str[i]);
            if (nxt[now][c])
            {   cnt++;
                now = nxt[now][c];
            }
            else
            {   while (now && !nxt[now][c]) now = fail[now];
                if (!now) { now = root; cnt = 0; }
                else { cnt = len[now] + 1; now = nxt[now][c]; }
            }
            val[i] = cnt;
        }
    }
} tzw;

int d[N];
int Q[N], hd, tl;
bool check(char *s, int lim)
{   int Len = strlen(s+1);
    for (int i = 0; i <= lim; i++) d[i] = 0;
    hd = 0, tl = 1;
    for (register int i = lim; i <= Len; i++)
    {   d[i] = d[i-1];
        if (i > lim) //这里一定不能去掉, 去掉会RE
        {   while (hd < tl && Q[hd] < i-val[i]) hd++;
            while (hd < tl && d[Q[tl-1]]+i-Q[tl-1] < d[i-lim]+i-(i-lim)) tl--;
            Q[tl++] = i - lim;
        }
        if (val[i] >= lim) d[i] = max(d[i], d[Q[hd]] + i - Q[hd]);
    }
    return 10*d[Len] >= 9*Len;
}

int solve(char *s)
{   int l = 1, r = strlen(s+1);
    while (l <= r)
    {   int mid = (l+r) >> 1;
        if (check(s, mid)) l = mid + 1;
        else r = mid - 1;
    }
    return r;
}

char str[N];

int main()
{   scanf("%d %d", &n, &m);
    tzw.init();
    for (register int i = 1; i <= m; i++)
    {   scanf("%s", str);
        tzw.insert(str);
    }
    for (register int i = 1; i <= n; i++)
    {   scanf("%s", str+1);
        tzw.work(str);
        if(!check(str, 1)) puts("0");
        else printf("%d\n", solve(str));
    }
    return 0;
}

备注

注释里的坑我全踩了

原文地址:https://www.cnblogs.com/2016gdgzoi509/p/8718220.html

时间: 2024-10-07 22:06:06

【BZOJ2806】【CTSC2012】Cheat 广义后缀自动机+二分+Dp的相关文章

【BZOJ2806】[Ctsc2012]Cheat 广义后缀自动机+二分+单调队列优化DP

[BZOJ2806][Ctsc2012]Cheat Description Input 第一行两个整数N,M表示待检查的作文数量,和小强的标准作文库的行数接下来M行的01串,表示标准作文库接下来N行的01串,表示N篇作文 Output N行,每行一个整数,表示这篇作文的Lo 值. Sample Input 1 2 10110 000001110 1011001100 Sample Output 4 HINT 输入文件不超过1100000字节 注意:题目有改动,可识别的长度不小于90%即可,而不是

E. Three strings 广义后缀自动机

http://codeforces.com/problemset/problem/452/E 多个主串的模型. 建立一个广义后缀自动机,可以dp出每个状态的endpos集合大小.同时也维护一个R[]表示那个串出现过. 所以可以算出每个状态的dp[i][k]表示第k个串在第i个状态中出现的次数. 可以知道sigma dp[i][0...k]是等于  endpos集合的大小. 然后把这个贡献加到min(i)....max(i)中去就可以了 差分一下. #include <bits/stdc++.h>

【BZOJ3926】[Zjoi2015]诸神眷顾的幻想乡 广义后缀自动机

[BZOJ3926][Zjoi2015]诸神眷顾的幻想乡 Description 幽香是全幻想乡里最受人欢迎的萌妹子,这天,是幽香的2600岁生日,无数幽香的粉丝到了幽香家门前的太阳花田上来为幽香庆祝生日. 粉丝们非常热情,自发组织表演了一系列节目给幽香看.幽香当然也非常高兴啦. 这时幽香发现了一件非常有趣的事情,太阳花田有n块空地.在过去,幽香为了方便,在这n块空地之间修建了n-1条边将它们连通起来.也就是说,这n块空地形成了一个树的结构. 有n个粉丝们来到了太阳花田上.为了表达对幽香生日的祝

BZOJ 3277 串 (广义后缀自动机)

3277: 串 Time Limit: 10 Sec Memory Limit: 128 MB Submit: 309 Solved: 118 [Submit][Status][Discuss] Description 字符串是oi界常考的问题.现在给定你n个字符串,询问每个字符串有多少子串(不包括空串)是所有n个字符串中至少k个字符串的子串(注意包括本身). Input 第一行两个整数n,k. 接下来n行每行一个字符串. Output 输出一行n个整数,第i个整数表示第i个字符串的答案. Sa

广义后缀自动机

1).自动机的介绍 首先我们先来介绍一下什么是自动机,有限状态自动机的功能是识别字符串,令一个自动机A,若他能识别字符串S,就记为A(S)=Ture,否则A(S)=False. 自动机由五个部分组成,alpha:字符集,state:状态集合,init:初始状态,end:结束状态集合,trans:状态转移函数. 令trans(s,ch)表示当前状态是s,在读入字符ch之后,所到达的状态.如果trans(s,ch)这个转移不存在,为了方便,设其为null,同时null只能转移到null.null表示

hdu 5853 Jong Hyok and String(广义后缀自动机)

题目链接:hdu 5853 Jong Hyok and String 题意: 给你n个字符串,m个询问,每次询问一个字符串 定义set(s)={(i,j)} 表示 s在第i个字符串中出现,且末尾位置为j. 对于一个询问,求set(Qi)=set(t) ,t串的数量. 题解: 如果是n=1,那么就是后缀自动机的一道裸题,答案就是Qi串匹配的最后一个节点x,ml[x]-ml[f[x]]. 现在是多个串,那么就建立一个广义后缀自动机.每次插入一个串后,将last=root,然后继续插下一个就行了. 最

【codeforces666E】Forensic Examination 广义后缀自动机+树上倍增+线段树合并

题目描述 给出 $S$ 串和 $m$ 个 $T_i$ 串,$q$ 次询问,每次询问给出 $l$ .$r$ .$x$ .$y$ ,求 $S_{x...y}$ 在 $T_l,T_{l+1},...,T_r$ 中的哪一个里出现次数最多,输出出现次数最多的串编号(如果有多个则输出编号最小的)以及相应出现次数. $|S|,q\le 5\times 10^5$ ,$\sum\limits_{i=1}^m|T_i|\le 5\times 10^4$ . 题解 广义后缀自动机+树上倍增+线段树合并 对 $S$

BZOJ 3926: [Zjoi2015]诸神眷顾的幻想乡 广义后缀自动机 后缀自动机 字符串

https://www.lydsy.com/JudgeOnline/problem.php?id=3926 广义后缀自动机是一种可以处理好多字符串的一种数据结构(不像后缀自动机只有处理一到两种的时候比较方便). 后缀自动机可以说是一种存子串的缩小点数的trie树,广义后缀自动机就是更改了一下塞点的方式让它可以塞多个子串. 1 #include<iostream> 2 #include<cstdio> 3 #include<algorithm> 4 #include<

广义后缀自动机模板

后缀自动机能解决很多单串的问题.但是一旦到了多串的情况.可能就会变得有些棘手 这个时候你可能会想能不能将多个串一起构建出和单串后缀自动机那样子拥有诸多优美性质的自动机呢? 答案当然是有的.那就是广义后缀自动机 对于广义后缀自动机.和普通的后缀自动机写法上有些许不同之处 大致就是在插入新串的时候.需要把当前状态指针 last 指回 root 还有一个问题.网上的人们都说广义后缀自动机在新插入节点的时候要判是否已经存在 这个就造成了代码的迥异 关于代码.借鉴了这个博客 ==> Click here