[CTSC2012]熟悉的文章 后缀自动机
题解:
观察到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]熟悉的文章 后缀自动机的更多相关文章
- [CTSC2012]熟悉的文章(后缀自动机+动态规划)
题目描述 阿米巴是小强的好朋友. 在小强眼中,阿米巴是一个作文成绩很高的文艺青年.为了获取考试作文的真谛,小强向阿米巴求教.阿米巴给小强展示了几篇作文,小强觉得这些文章怎么看怎么觉得熟悉,仿佛是某些范 ...
- P4022 [CTSC2012]熟悉的文章
题目 P4022 [CTSC2012]熟悉的文章 题目大意:多个文本串,多个匹配串,我们求\(L\),\(L\)指(匹配串中\(≥L\)长度的子串出现在文本串才为"熟悉",使得匹配 ...
- [CTSC2012]熟悉的文章(广义后缀自动机+二分答案+单调队列优化DP)
我们对作文库建出广义后缀自动机.考虑用\(SAM\)处理出来一个数组\(mx[i]\),表示从作文的第\(i\)个位置向左最远在作文库中出现的子串的长度.这个东西可以在\(SAM\)上跑\(trans ...
- 题解-CTSC2012 熟悉的文章
Problem bzoj 题目大意:给定多个标准串和一个文本串,全部为01串,如果一个串长度不少于\(L\)且是任意一个标准串的子串,那么它是"熟悉"的.对于文本串\(A\),把\ ...
- CTSC2012 熟悉的文章
传送门 首先很容易想到对于所有的模式串建出广义后缀自动机,之后对于我们每一个要检查的文本串,先在SAM上跑,计算出来每一个位置能匹配到的最远的位置是多少.(就是当前点减去匹配长度) 之后--考虑DP- ...
- 【[CTSC2012]熟悉的文章】
题目 好题啊 \(SAM\)+单调队列优化\(dp\) 首先这个\(L\)满足单调性真是非常显然我们可以直接二分 二分之后套一个\(dp\)就好了 设\(dp[i]\)表示到达\(i\)位置熟悉的文章 ...
- Luogu-4022 [CTSC2012]熟悉的文章
广义后缀自动机+DP 对于作文库建出广义后缀自动机,广义自动机就是在每次添加一个字符串之前把\(last=0\),然后正常添加就好了 对于每个询问串,预处理出每个位置\(i\)能向前匹配的最长长度\( ...
- [BZOJ2806][CTSC2012]熟悉的文章(Cheat)
bzoj luogu 题目描述 阿米巴是小强的好朋友. 在小强眼中,阿米巴是一个作文成绩很高的文艺青年.为了获取考试作文的真谛,小强向阿米巴求教.阿米巴给小强展示了几篇作文,小强觉得这些文章怎么看怎么 ...
- 【BZOJ2806】【CTSC2012】Cheat 广义后缀自动机+二分+Dp
题目 题目在这里 思路&做法 我们先对标准作文库建广义后缀自动机. 然后对于每一篇阿米巴的作文, 我们首先把放到广义后缀自动机跑一遍, 对于每一个位置, 记录公共子串的长度\((\)即代码和下 ...
随机推荐
- Linux文件归档和解压缩
1.tar tar命令相当于归档,不做压缩,解压同样也是把归档文件释放出来(归档通俗上可以理解为把文件分类,把一些文件放到一个包中归类,方便用户管理) 解包:tar -zxvf file.tar #解 ...
- 堆中的路径(MOOC)
将一系列给定数字插入一个初始为空的小顶堆H[].随后对任意给定的下标i,打印从H[i]到根结点的路径. 输入格式: 每组测试第1行包含2个正整数N和M(≤),分别是插入元素的个数.以及需要打印的路径条 ...
- conda环境管理
查看环境 conda env list 创建环境 conda create -n python36 python=3.6 进入环境 source activate python36 activate ...
- Wacom发布Cintiq Companion 2
全新的Cintiq Companion 2是一款强大的平板电脑,让创意专业人士获得最佳的屏幕笔触,让创意随时随地进行.用户还可以在家中或工作时连接到Mac或PC电脑获得无与伦比的灵活性! 2015年1 ...
- spark的数据结构 RDD——DataFrame——DataSet区别
转载自:http://blog.csdn.net/wo334499/article/details/51689549 RDD 优点: 编译时类型安全 编译时就能检查出类型错误 面向对象的编程风格 直接 ...
- nginx 根据get参数重定向(根据电视访问的mac地址传递的值,来重定向访问别的url地址,这样就可以进行单台的测试环境。。)
背景是这样的: 公司要做所有客户端的迁移到别的云平台,但又担心会有问题,所以考虑分批次迁移过去,这样就需要迁移部分用户,因为客户端刷但都是统一但rom包,不能轻易发生改动,所以决定用重定向方式将部分客 ...
- 20181016-4 Alpha阶段第2周/共2周 Scrum立会报告+燃尽图 05
作业要求参见:https://edu.cnblogs.com/campus/nenu/2018fall/homework/2288 Scrum master:王硕 一.小组介绍 组长:王一可 组员:范 ...
- 第五周PSP作业
PSP表格: 累积进度条: 折线图: 饼状图:
- 关于解决MySort
关于解决MySort 那天老师教给我们关于sort的用法以及String类中的split方法.在一定程度上告诉我们sort用法的原理和一些特别的用法后,老师叫我们用JAVA尝试去设计一个"M ...
- 按照Right-BICEP要求设计四则运算3程序的单元测试用例
按照Right-BICEP要求: Right——结果是否正确? B——是否所有的边界条件都是正确的? I——能查一下反响关联吗? C——能用其它手段交叉检查一下吗? E——你是否可以强制错误条件发生? ...