题面:洛谷

题解:

  观察到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. js页面动态时间展示

    效果图: 具体代码 js代码 <script type="text/javascript"> var t = null; t = setTimeout(time,100 ...

  2. asp.net core 2.2 根据PC端和移动端自动显示不同视图而不改变url地址

    1.添加HttpRequest扩展方法 public static class RequestExtensions { //regex from http://detectmobilebrowsers ...

  3. avaweb(三十二)——JDBC学习入门

    一.JDBC相关概念介绍 1.1.数据库驱动 这里的驱动的概念和平时听到的那种驱动的概念是一样的,比如平时购买的声卡,网卡直接插到计算机上面是不能用的,必须要安装相应的驱动程序之后才能够使用声卡和网卡 ...

  4. 车架号识别,VIN码识别 助力汽车后市场

    又有一家汽配圈新贵引入了小译家的 车架号识别(VIN码识别)技术 那就是明觉科技 是一个服务于汽车后市场 集数据服务.行业数据挖掘 及“互联网+”为一体的汽配信息协作平台 旗下拥有一款全车零配件信息智 ...

  5. hession矩阵的计算与在图像中的应用

    参考的一篇博客,文章地址:https://blog.csdn.net/lwzkiller/article/details/55050275 Hessian Matrix,它有着广泛的应用,如在牛顿方法 ...

  6. xshell—实现Linux与Windows之间的文件传递

    在Windows系统上,通过xshell连接Linux系统. 第一种使用方式:从Linux系统上下载文件到Windows系统. 准备工作: $ sudo apt-get install lrzsz 安 ...

  7. JS对字符串编码的几种方式

    函数 描述 encodeURI() 把字符串编码为 URI encodeURIComponent() 把字符串编码为 URI 组件 escape() 对字符串进行编码 上面是查询来自w3school的 ...

  8. leetcode个人题解——#34 Find First and Last Position of Element in Sorted Array

    思路:先二分查找到一个和target相同的元素,然后再左边二分查找左边界,右边二分查找有边界. class Solution { public: , end = -; int ends; int lS ...

  9. 使用qemu启动dd制作的img镜像

    1. 准备工作 应用场景 在需要单机取证时,需要在不影响对象业务的情况下进行取证或分析,可以使用dd 对目标服务器进行镜像,生成img文件,镜像可以通过winhex进行静态分析.但是想要动态分析服务器 ...

  10. spark总结——转载

    转载自:    spark总结 第一个Spark程序 /** * 功能:用spark实现的单词计数程序 * 环境:spark 1.6.1, scala 2.10.4 */ // 导入相关类库impor ...