题面:洛谷

题解:

  观察到L是可二分的,因此我们二分L,然后就只需要想办法判断这个L是否可行即可。

  因为要尽量使L可行,因此我们需要求出对于给定L,这个串最多能匹配上多少字符。

  如果我们可以对每个位置i求出g[i]表示以这个位置为结尾,向前最多匹配多少位,就可以快速得知任意区间[l, r]是否可以被匹配上,因为一个串如果可以被匹配上,那么它的子串肯定也可以被匹配上。

  然后我们再做一次DP,设f[i]为DP到i位,最多能匹配上多少字符

  那么朴素做法就是枚举上一段的结尾,然后更新,不过注意到这个决策是单调的,因此可以用单调队列优化一下。

  因为有g[i]和mid的限制,所以我们可以选取的上一段结尾的区间是[i - g[i], i - mid].

  所以在用单调队列维护的时候,为了保证右端点合法,每次不能立即把当前的i加入队列,而是要在下次枚举到区间右端点已经包括了i的时候再把i加入队列。(类似与NOIP普及组跳房子)

  

  这样就可以O(n)的求解。

  不过首先还需要求出g[i]....

  那么g[i]怎么求呢?  

  我们先建立广义后缀自动机,然后在自动机上匹配大串,假设当前匹配到的节点是now,上一次的长度是rnt。

  那么我们只有沿着parent树向上走,才可以保证l[当前节点]是合法的。

  因此我们不断向上走,每走到一个节点,都更新rnt = l[now], 直到走到一个节点使得它有当前字符对应的边,这个时候我们把rnt更新为rnt+1,并更新g数组

 #include<bits/stdc++.h>
using namespace std;
#define R register int
#define AC 1100100
#define ac 2300000 int n, m, tail, head, len;
int g[AC], f[AC], q[AC];//g[i]表示以i为结尾,最长能匹配的长度
char s[AC];//f[i]表示DP到i位,能被覆盖的最大长度 inline int read()
{
int x = ;char c = getchar();
while(c > '' || c < '') c = getchar();
while(c >= '' && c <= '') x = x * + c - '', c = getchar();
return x;
} inline void upmax(int &a, int b)
{
if(b > a) a = b;
} struct sam_auto{
int ch[ac][], l[ac], fa[ac], last, cnt; void add(int c)
{
int p = last, np = ++ cnt;
last = cnt, l[np] = l[p] + ;
for( ; !ch[p][c]; p = fa[p]) ch[p][c] = np;
if(!p) fa[np] = ;
else
{
int q = ch[p][c];//找到对应节点
if(l[p] + == l[q]) fa[np] = q;
else
{
int nq = ++ cnt;
l[nq] = l[p] + ;
memcpy(ch[nq], ch[q], sizeof(ch[q]));
fa[nq] = fa[q];
fa[q] = fa[np] = nq;
for( ; ch[p][c] == q; p = fa[p]) ch[p][c] = nq;
}
}
} void build()
{
cnt = ;
for(R i = ; i <= m; i ++)
{
last = , scanf("%s", s + ), len = strlen(s + );
for(R j = ; j <= len; j ++) add(s[j] - '');
}
} void get_g()//求g数组
{
int now = , rnt = ;
/*for(R i = 1; i <= len; i ++)
{//要先更新g再向下跳,因为根据parent树的性质,只有从一个点向上走才能保证
int v = s[i] - '0';//这个点中出现的串在遇到的点中都出现了,如果先向下走了就无法保证了
while(now != 1 && !ch[now][v]) now = fa[now];//一直向上跳到可以匹配为止
if(ch[now][v]) g[i] = l[now] + 1, now = ch[now][v];
}*/
for(R i = ; i <= len; i ++)
{
int v = s[i] - '';//因为当前点是上一个点往下走走到的, 所以当前点的l[now]其实不一定合法。。。只能保证l[fa[now]]合法
while(now != && !ch[now][v]) now = fa[now], rnt = l[now];//因此如果这个点是有v这个节点的话,也不能直接取l[now],
if(ch[now][v]) g[i] = ++rnt, now = ch[now][v];//而是要保留上次的匹配长度
else g[i] = , rnt = ;
//printf("%d ", g[i]);
}
//printf("\n");
}
}sam; void pre()
{
n = read(), m = read();
sam.build();
} bool check(int mid)//上一段结尾区间[i - g[i], i - mid]
{
int last = ;
head = tail = ;
for(R i = ; i <= len; i ++)
{
f[i] = f[i - ];//因为如果强制取区间内的值,就相当于强制当这个点必须能产生贡献就产生贡献,但是实际上不产生贡献,直接取f[i -1]可能会更优
if(i - g[i] > i - mid) continue;//如果没有合法区间就只能这样了
while(last < i - mid)//每次加入不能超过右区间以保证合法
{
++ last;
while(head <= tail && f[q[tail]] - q[tail] < f[last] - last) -- tail;
q[++ tail] = last;
}
while(head <= tail && q[head] < i - g[i]) ++ head;//把不属于合法区间的都去掉
//printf("%d ", q[head]);//去掉非法区间的应该放在后面,因为后面还有加点操作,可能会加入非法节点
upmax(f[i], f[q[head]] + i - q[head]);
}
//printf("\n");
return f[len] * >= len * ;
} int half()//l显然满足可二分性
{
int l = , r = len, mid;
while(l < r)
{
mid = (l + r + ) >> ;
if(check(mid)) l = mid;
else r = mid - ;
}
return l;
} void work()
{
for(R i = ; i <= n; i ++)
{
scanf("%s", s + ), len = strlen(s + );
sam.get_g(), printf("%d\n", half());
//for(R j = 1; j <= n; j ++) g[j] = f[j] = 0;
}
} int main()
{
// freopen("in.in", "r", stdin);
pre();
work();
// fclose(stdin);
return ;
}

  

  

[CTSC2012]熟悉的文章 后缀自动机的更多相关文章

  1. [CTSC2012]熟悉的文章(后缀自动机+动态规划)

    题目描述 阿米巴是小强的好朋友. 在小强眼中,阿米巴是一个作文成绩很高的文艺青年.为了获取考试作文的真谛,小强向阿米巴求教.阿米巴给小强展示了几篇作文,小强觉得这些文章怎么看怎么觉得熟悉,仿佛是某些范 ...

  2. P4022 [CTSC2012]熟悉的文章

    题目 P4022 [CTSC2012]熟悉的文章 题目大意:多个文本串,多个匹配串,我们求\(L\),\(L\)指(匹配串中\(≥L\)长度的子串出现在文本串才为"熟悉",使得匹配 ...

  3. [CTSC2012]熟悉的文章(广义后缀自动机+二分答案+单调队列优化DP)

    我们对作文库建出广义后缀自动机.考虑用\(SAM\)处理出来一个数组\(mx[i]\),表示从作文的第\(i\)个位置向左最远在作文库中出现的子串的长度.这个东西可以在\(SAM\)上跑\(trans ...

  4. 题解-CTSC2012 熟悉的文章

    Problem bzoj 题目大意:给定多个标准串和一个文本串,全部为01串,如果一个串长度不少于\(L\)且是任意一个标准串的子串,那么它是"熟悉"的.对于文本串\(A\),把\ ...

  5. CTSC2012 熟悉的文章

    传送门 首先很容易想到对于所有的模式串建出广义后缀自动机,之后对于我们每一个要检查的文本串,先在SAM上跑,计算出来每一个位置能匹配到的最远的位置是多少.(就是当前点减去匹配长度) 之后--考虑DP- ...

  6. 【[CTSC2012]熟悉的文章】

    题目 好题啊 \(SAM\)+单调队列优化\(dp\) 首先这个\(L\)满足单调性真是非常显然我们可以直接二分 二分之后套一个\(dp\)就好了 设\(dp[i]\)表示到达\(i\)位置熟悉的文章 ...

  7. Luogu-4022 [CTSC2012]熟悉的文章

    广义后缀自动机+DP 对于作文库建出广义后缀自动机,广义自动机就是在每次添加一个字符串之前把\(last=0\),然后正常添加就好了 对于每个询问串,预处理出每个位置\(i\)能向前匹配的最长长度\( ...

  8. [BZOJ2806][CTSC2012]熟悉的文章(Cheat)

    bzoj luogu 题目描述 阿米巴是小强的好朋友. 在小强眼中,阿米巴是一个作文成绩很高的文艺青年.为了获取考试作文的真谛,小强向阿米巴求教.阿米巴给小强展示了几篇作文,小强觉得这些文章怎么看怎么 ...

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

    题目 题目在这里 思路&做法 我们先对标准作文库建广义后缀自动机. 然后对于每一篇阿米巴的作文, 我们首先把放到广义后缀自动机跑一遍, 对于每一个位置, 记录公共子串的长度\((\)即代码和下 ...

随机推荐

  1. 向大学说拜拜——大学 > 兴趣 + 时间 + 思考 + 实践

    [人物素描] 大学期间,担任过班委,加入过学生会,参加过社团,拿过奖学金......而印象最深刻的莫过于参加并组织过ACM集训,以及参加过导师的国家自然科学基金项目了.毕业时顺利拿到一波offer,并 ...

  2. 【搜索好题】bzoj1501 [NOI2005]智慧珠游戏

    bzoj1501 [NOI2005]智慧珠游戏 搜索苟逼题系列. 暴力枚举每一种情况(包括旋转翻转全都考虑在内)然后码出代码. (正解似乎不是这样子的) 那年好像还有平衡树苟逼题维护数列233333心 ...

  3. SQL查找重复项目

    1 2 3 4 5 6 7 SELECT t1.* FROM t1,   (SELECT name,ADD    FROM t1    GROUP BY name,ADD HAVING COUNT(1 ...

  4. TPO-17 C1 Find materials for an opera paper

    TPO-17 C1 Find materials for an opera paper production n. 成果:产品:生产:作品 第 1 段 1.Listen to a conversati ...

  5. 240. 搜索二维矩阵 II

    二维数组搜索 编写一个高效的算法来搜索 m x n 矩阵 matrix 中的一个目标值 target.该矩阵具有以下特性: 每行的元素从左到右升序排列. 每列的元素从上到下升序排列. 示例: 现有矩阵 ...

  6. HP VC模块Shared uplink Sets配置参考

    首先配置MAC地址的分配方式 在左侧导航栏中,点解"MAC Addresses" 选择VC分配MAC地址,并且选择一个合适的地址段,点击"Apply"继续 在弹 ...

  7. Ubuntu系统下在PyCharm里用virtualenv集成TensorFlow

    我的系统环境 Ubuntu 18.04 Python3.6 PyCharm 2018.3.2 community(免费版) Java 1.8 安装前准备 由于众所周知的原因,安装中需要下载大量包,尽量 ...

  8. Python模块random使用详情

    python常用模块目录 1.random.random()#用于生成一个0到1的随机浮点数:0<= n < 1.0 import random mcw = random.random() ...

  9. 原生js和jquey获取窗口宽高,滚动条,鼠标位置总结

    JQuery获取浏览器窗口的可视区域高度和宽度,滚动条高度   alert($(window).height()); //浏览器时下窗口可视区域高度 alert($(document).height( ...

  10. 面试Tips

    面试Tips 面向对象:准备找工作的同学 内容概述:关于面试的一些经验总结,希望能带给你些许帮助.若有描述不准确的地方,欢迎指点建议. 内容提炼:共分为四阶段 1.面试前之静生慧 (1)课本知识过一遍 ...