首页 > 代码库 > bzoj 2806: [Ctsc2012]Cheat 熟悉的文章

bzoj 2806: [Ctsc2012]Cheat 熟悉的文章

【题意】

技术分享

 

【题解】

fhq自己写的题解就很清楚啦 (里面有关于神秘的阿米巴同学的介绍哦!很帅的样子,,,

我再来理一下思路

这道题思路应该是很简单很常规的,,但我做题太少辣!

二分一下肯定是必要的, 下面是判断是否可行的问题。

容易想到 要开一个 f 数组 表示 扫到了 第 i 位时 能匹配上多少个字母, 最后答案就是 f[lenth] * 10 >= lenth * 9 啦

f[i] 显然可以 通过 f[j] 转移 (j <= i - 二分出的下限的长度 + 1  且s[j……i]出现在字典里)

下一步要确定的就是 有哪些 j 是出现在字典里的, 也就是 在 S 中 以 i 为后缀的这个字符串 最长有多少出现在字典里。

后缀自动机模板就出来啦 (此处该有音乐: 当(sou)当(sou)当(sou)当(?mi)……

然后会发现 dp 的时候 j 显然是单调的(如果 i - 1 的转移时的下限 为 j , i 的显然不会小于 j , 否则 i - 1 的也可以小于j 了)

所以单调队列优化一下就好了

 

代码非常非常短

#include <iostream>#include <cstdio>#include <cmath>#include <algorithm>#include <cstring>#define MAXN 3000005using namespace std;int n, m, lenth, cnt, last, q[MAXN], len[MAXN], trans[MAXN][3], fat[MAXN], f[MAXN];char s[MAXN];void insert(int x){    int np = ++ cnt, p = last;    len[np] = len[last] + 1; last = np;    for(; !trans[p][x]; p = fat[p])trans[p][x] = np;    if(!p)fat[np] = 1;    else{        int q = trans[p][x];            if(len[q] == len[p] + 1)fat[np] = q;        else{            int nq = ++ cnt; len[nq] = len[p] + 1;            memcpy(trans[nq], trans[q], sizeof(trans[q]));            fat[nq] = fat[q];            fat[q] = fat[np] = nq;            for(; trans[p][x] == q; p = fat[p])trans[p][x] = nq;            }    }   }bool pd(int limit){    int bot = 1, top = 0, cur = 1, ll = 0; f[0] = 0;    for(int i = 1; i <= lenth; i ++){        f[i] = f[i - 1];        int c = s[i] - ‘0‘;        for(; !trans[cur][c]; cur = fat[cur]);        if(!cur){cur = 1; ll = 0;}        ll = min(ll, len[cur]) + 1;        cur = trans[cur][c];        int p = i - limit;        if(p >= 0){            while(bot <= top && q[top] - f[q[top]] > p - f[p]) top --;            q[++ top] = p;            }        while(bot <= top && q[bot] < i - ll)bot ++;        if(bot <= top) f[i] = max(f[i], f[q[bot]] + i - q[bot]);    } return f[lenth] * 10 >= lenth * 9;}int main(){    scanf("%d%d", &n, &m);    last = ++ cnt;    while(m --){        scanf("%s", s + 1); lenth = strlen(s + 1);        for(int i = 1; i <= lenth; i ++)insert(s[i] - ‘0‘);        insert(2);        }    while(n --){        scanf("%s", s + 1); lenth = strlen(s + 1);        int l = 1, r = lenth, mid;         while(l + 1 < r){            mid = l + r >> 1;            if(pd(mid))l = mid;            else r = mid - 1;            }printf("%d\n", pd(r) ? r : l);    }    return 0;}

 

bzoj 2806: [Ctsc2012]Cheat 熟悉的文章