题面:洛谷

题解:

  其实还可以用AC自动机做,但是没调出来,,,不知道发生了什么。。。

  AC自动机做法如下:

    观察到如果我们对给定的每个串建AC自动机,那么直接拿大串在上面匹配,如果遇到了一个单词的终止节点,假设当前大串的位置是i,匹配到的节点是j,那么这个单词覆盖了

    [i - dep[j] + 1, i]这个区间(dep[j]即为单词长度)

    但是我们发现空间根本开不下。

    因为对每个串分别匹配不会影响结果,所以考虑每50个串我们重建一次AC自动机,然后匹配一次。

    匹配中的每次区间修改都用差分维护,那么最后对于每个权值大于等于1的位置都统计1 的贡献即为答案。

  后缀自动机做法如下:

    考虑统计对于每个位置而言,以i为结尾的最长被覆盖的长度

    我们对大串建立后缀自动机,然后对于每个小串都拿去自动机上匹配。假设我们最后匹配到的状态为x,既然这个状态已经被覆盖了,那么x在parent树上的子树也一定可以被覆盖。

    但是要对每个状态内的最长串长度MAXS取min,因为匹配到一个状态只能代表可以覆盖这个状态所代表的子串,且长度不能大于当前给定图案(小串)的长度。

    那么我们对每次匹配到的状态x打上标记,标记权值为当前小串的长度,如果有多个标记,取最大的那个。

    最后统计打完所有标记之后,对于每个节点都下传标记,下传的过程中依然对标记权值取max。

    最后即可得到对于每个位置而言,以i为结尾的最长被覆盖的长度。

    

    一个快速(好写)统计的方法:

      因为对于每个点下传标记就相当于对每个点接收来自它父亲的标记,那么我们只需要保证在接收点x的父亲的标记时,fa[x]已经接收过来自fa[fa[x]]的标记。

      因此我们按bfs序来更新标记就可以了,因为l[fa[x]](l[i]表示状态i的MAXS,即最长的保证right集合不变的长度)一定小于l[x],所以我们按照l[x]的大小来确定顺序即可。

  

 #include<bits/stdc++.h>
using namespace std;
#define R register int
#define AC 301000
#define ac 601000 int n, m, ans;
int b[AC], k[ac];
int d[AC], mark[ac];//差分数组
char s[AC]; inline void upmax(int &a, int b){
if(b > a) a = b;
} struct sam_atm{
int last, cnt;
int ch[ac][], fa[ac], l[ac], right[ac]; inline void add(int c, int i)
{
int p = last, np = ++ cnt;
last = np, l[np] = l[p] + , right[np] = i;
for( ; p && !ch[p][c]; p = fa[p]) ch[p][c] = np;
if(!p) fa[np] = ;
else
{
int q = ch[p][c];//获取第一个有c的节点的对应边所指向的节点
if(l[p] + == l[q]) fa[np] = q;//如果不会造成影响,那么就直接连fa
else
{
int nq = ++ cnt;//否则就再建新点
l[nq] = l[p] + ;
memcpy(ch[nq], ch[q], sizeof(ch[q]));//把q的信息赋值给nq,,,接下来nq相当于要取代q了
fa[nq] = fa[q], fa[q] = fa[np] = nq;
for( ; ch[p][c] == q; p = fa[p]) ch[p][c] = nq;
}
}
} void find()
{
int len = strlen(s + ), now = ;
for(R i = ; i <= len; i ++)
{
if(!ch[now][s[i] - 'a']) return ;
now = ch[now][s[i] - 'a'];
}
upmax(mark[now], len);
} void work()
{
for(R i = ; i <= cnt; i ++) ++ b[l[i]];
for(R i = ; i <= n; i ++) b[i] += b[i - ];//这里只需要枚举到n,多枚举就re了
for(R i = ; i <= cnt; i ++) k[b[l[i]] --] = i;//为i赋b[l[i]]的排名
for(R i = ; i <= cnt; i ++) upmax(mark[k[i]], mark[fa[k[i]]]); for(R i = ; i <= cnt; i ++) //只能用有right的叶子节点更新
if(right[i]) ++ d[right[i] - mark[i] + ], -- d[right[i] + ];
for(R i = ; i <= n; i ++) d[i] += d[i - ];
for(R i = ; i <= n; i ++) ans += (d[i] > );
printf("%d\n", n - ans);
}
}T; void pre()
{
T.last = T.cnt = ;//这个又忘了。。。
scanf("%d%s", &n, s + );
for(R i = ; i <= n; i ++) T.add(s[i] - 'a', i);
scanf("%d", &m);
for(R i = ; i <= m; i ++) scanf("%s", s + ), T.find();
} int main()
{
// freopen("in.in", "r", stdin);
pre();
T.work();
// fclose(stdin);
return ;
}

[COCI2011-2012#5] POPLOCAVANJE 后缀自动机的更多相关文章

  1. luogu4595 [COCI2011-2012#5] POPLOCAVANJE 后缀自动机

    看着就像后缀自动机.... 然后搜了一下,网上一大把的\(AC\)自动机 嗯...... 不管了,打一个试试 然后就过了\(QAQ\) 我们考虑对于每个点\(i\)求出它往前最长能匹配的子串的长度 可 ...

  2. HDU 4436 str2int(后缀自动机)(2012 Asia Tianjin Regional Contest)

    Problem Description In this problem, you are given several strings that contain only digits from '0' ...

  3. hdu4436-str2int(后缀数组 or 后缀自动机)

    题意:给你一堆字符串,仅包含数字'0'到'9'. 例如 101 123 有一个字符串集合S包含输入的N个字符串,和他们的全部字串. 操作字符串很无聊,你决定把它们转化成数字. 你可以把一个字符串转换成 ...

  4. 后缀自动机(SAM)

    *在学习后缀自动机之前需要熟练掌握WA自动机.RE自动机与TLE自动机* 什么是后缀自动机 后缀自动机 Suffix Automaton (SAM) 是一个用 O(n) 的复杂度构造,能够接受一个字符 ...

  5. 字符串(多串后缀自动机):HDU 4436 str2int

    str2int Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 131072/131072 K (Java/Others)Total S ...

  6. 后缀自动机/回文自动机/AC自动机/序列自动机----各种自动机(自冻鸡) 题目泛做

    题目1 BZOJ 3676 APIO2014 回文串 算法讨论: cnt表示回文自动机上每个结点回文串出现的次数.这是回文自动机的定义考查题. #include <cstdlib> #in ...

  7. 回文树&后缀自动机&后缀数组

    KMP,扩展KMP和Manacher就不写了,感觉没多大意思.   之前感觉后缀自动机简直可以解决一切,所以不怎么写后缀数组.   马拉车主要是通过对称中心解决问题,有的时候要通过回文串的边界解决问题 ...

  8. HDU 1403 Longest Common Substring(后缀自动机——附讲解 or 后缀数组)

    Description Given two strings, you have to tell the length of the Longest Common Substring of them. ...

  9. HDU 4436 (后缀自动机)

    HDU 4436 str2int Problem : 给若干个数字串,询问这些串的所有本质不同的子串转换成数字之后的和. Solution : 首先将所有串丢进一个后缀自动机.由于这道题询问的是不同的 ...

随机推荐

  1. cogs1439 货车运输

    cogs1439 货车运输 一道傻逼板子题. 边一定在最大生成树上,这个可以用消圈证明 然后kruskal跑一遍再搜一遍再建ST表再跑LCA这题就做完了. RT PS.交上去的代码把Kruskal打成 ...

  2. jQuery Validate (登录页面相关验证)

    $(function() { var submit = false; var superHtml = []; /** * 匹配企业帐号,以字母开头,长度在6-20之间,只能包含字符.数字和下划线. * ...

  3. mysqldump: Got errno 28 on write(mysql)

    使用mysqldump进行数据库备份的时候,出现下面的错误 mysqldump: Got errno 28 on write   上网查了一下:出现这个错误,是因为磁盘的空间不足   再看系统空间使用 ...

  4. WPF RegisterAttached ListBoxItem(附加属性传递到Item)

    /// <summary> /// Controls的附加属性 /// </summary> public class ControlsAttached : Dependenc ...

  5. abp 关闭审计日志

    在WebProject.Web.Mvc项目中的Startup文件夹下中的WebProjectWebMvcModule.cs public override void PreInitialize() { ...

  6. 那些年安装Appium遇到的坑

      安装appium以及相关的总体记录   1 主要流程是参照这个来 https://www.cnblogs.com/wangyinghao/p/5780151.html 细节参考虫师的博客 http ...

  7. [C++]boost dijkstra获得两点间的最短路

    需求是只需要得到两点间的最短路,不需要求得单源对于全图的最短路,使用boost中的dijsktra_shortest_path,当得到目标点的最短路时直接throw exception. #inclu ...

  8. 【数据结构系列】线段树(Segment Tree)

    一.线段树的定义 线段树,又名区间树,是一种二叉搜索树. 那么问题来了,啥是二叉搜索树呢? 对于一棵二叉树,若满足: ①它的左子树不空,则左子树上所有结点的值均小于它的根结点的值 ②若它的右子树不空, ...

  9. [leetcode-914-X of a Kind in a Deck of Cards]

    In a deck of cards, each card has an integer written on it. Return true if and only if you can choos ...

  10. 基于spec评论作品 - 探路者 贪吃蛇

    基于spec评论作品,试用(并截图)所有其他小组的Alpha作品,与软件功能说明书对比,评论Alpha作品对软件功能说明书的实现. 首先通过命令行进入到游戏主页面中. 因为软件没有编译为exe程序,所 ...