原题链接,不是权限题

题目大意

有\(n\)个模板串,让你构造一个尽量长的串,使得这个串中任意一个长度为\(k\)的子串都是至少一个模板串的子串

题解

可以先看一下这道题 [POI2000]病毒

虽然是个\(AC\)自动机,不过思路很像

对于这道题,我们只需要把广义\(SAM\)建出来,然后在那些只经过\(maxlen\geqslant k\)的结点的路径中选一个最长的就行了。最后一步可以用拓扑排序来完成

拓扑建边时可以直接向\(fail\)连边,而不是把儿子补全(像\(AC\)自动机那样\(ch[u][c]=ch[fail[u]][c]\)),这样能降低复杂度

最后如果出现环,就输出\(INF\),否则求最长路径,注意特判所有结点的\(maxlen\)都小于\(k\)的情况,题目最下方有说明

丑的一批的代码奉上:

#include <algorithm>
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <string>
#include <vector>
#include <cmath>
#include <ctime>
#include <queue>
#include <map>
#include <set> using namespace std; #define ull unsigned long long
#define pii pair<int, int>
#define mii map<int, int>
#define uint unsigned int
#define lbd lower_bound
#define ubd upper_bound
#define ll long long
#define mp make_pair
#define pb push_back
#define re register
#define il inline #define N 100000 int n, k;
char s[N+5];
int root, nid, last, ch[2*N+5][26], fail[2*N+5], len[2*N+5];
int in[2*N+5], d[2*N+5];
vector<pii> G[2*N+5]; void clear() {
root = nid = 1;
memset(ch[1], 0, sizeof ch[1]);
memset(fail, 0, sizeof fail);
memset(in, 0, sizeof in);
memset(d, 0, sizeof d);
// memset(len, 0, sizeof len);
G[1].clear(), G[2*N+2].clear(), G[2*N+3].clear();
} void init() {
last = root;
} void extend(int c) {
int cur = ++nid;
memset(ch[cur], 0, sizeof ch[cur]);
G[cur].clear();
len[cur] = len[last]+1;
while(last && !ch[last][c]) ch[last][c] = cur, last = fail[last];
if(!last) fail[cur] = root;
else {
int p = last, q = ch[last][c];
if(len[q] == len[p]+1) fail[cur] = q;
else {
int clone = ++nid;
G[clone].clear();
len[clone] = len[p]+1;
for(int i = 0; i < 26; ++i) ch[clone][i] = ch[q][i];
fail[clone] = fail[q], fail[q] = fail[cur] = clone;
while(p && ch[p][c] == q) ch[p][c] = clone, p = fail[p];
}
}
last = cur;
} int topo() {
int S = 2*N+2, T = 2*N+3, ans = 0, cnt = 0;
d[T] = 0;
G[S].clear();
for(int i = 1; i <= nid; ++i)
if(len[i] == k-1) {
for(int j = 0; j < 26; ++j) if(ch[i][j]) G[i].pb(mp(ch[i][j], 1)), in[ch[i][j]]++;
in[i]++;
G[S].pb(mp(i, len[i]));
}
else if(len[i] >= k) {
for(int j = 0; j < 26; ++j) if(ch[i][j]) G[i].pb(mp(ch[i][j], 1)), in[ch[i][j]]++;
if(fail[i] && len[fail[i]] >= k-1) G[i].pb(mp(fail[i], 0)), in[fail[i]]++;
G[i].pb(mp(T, 0));
G[S].pb(mp(i, len[i]));
in[T]++, in[i]++;
cnt++;
}
if(!cnt) return k-1;
queue<int> q;
q.push(S);
while(!q.empty()) {
int u = q.front(); q.pop();
ans = max(ans, d[u]);
for(int i = 0; i < G[u].size(); ++i) {
int v = G[u][i].first, w = G[u][i].second;
in[v]--;
d[v] = max(d[v], d[u]+w);
if(!in[v]) q.push(v);
}
}
for(int i = 1; i <= nid; ++i) if(in[i]) return -1;
return ans;
} int main() {
while(~scanf("%d%d", &n, &k)) {
clear();
for(int i = 1; i <= n; ++i) {
scanf("%s", s);
int len = strlen(s);
init();
for(int j = 0; j < len; ++j) extend(s[j]-'a');
}
int ans = topo();
if(ans == -1) printf("INF\n");
else printf("%d\n", ans);
}
return 0;
}

BZOJ5261 Rhyme--广义SAM+拓扑排序的更多相关文章

  1. BZOJ2938 [Poi2000]病毒 和 BZOJ5261 Rhyme

    [Poi2000]病毒 二进制病毒审查委员会最近发现了如下的规律:某些确定的二进制串是病毒的代码.如果某段代码中不存在任何一段病毒代码,那么我们就称这段代码是安全的.现在委员会已经找出了所有的病毒代码 ...

  2. CodeForces - 666E: Forensic Examination (广义SAM 线段树合并)

    题意:给定字符串S,然后M个字符串T.Q次询问,每次给出(L,R,l,r),问S[l,r]在L到R这些T字符串中,在哪个串出现最多,以及次数. 思路:把所有串建立SAM,然后可以通过倍增走到[l,r] ...

  3. 【BZOJ 3473】 字符串 (后缀数组+RMQ+二分 | 广义SAM)

    3473: 字符串 Description 给定n个字符串,询问每个字符串有多少子串(不包括空串)是所有n个字符串中至少k个字符串的子串? Input 第一行两个整数n,k. 接下来n行每行一个字符串 ...

  4. Luogu P3181 [HAOI2016]找相同字符 广义$SAM$

    题目链接 \(Click\) \(Here\) 设一个串\(s\)在\(A\)中出现\(cnt[s][1]\)次,在\(B\)中出现\(cnt[s][2]\)次,我们要求的就是: \[\sum cnt ...

  5. CF666E Forensic Examination 广义SAM、线段树合并、倍增、扫描线

    传送门 朴素想法:对\(M\)个匹配串\(T_1,...,T_M\)建立广义SAM,对于每一次询问,找到这个SAM上\(S[pl...pr]\)对应的状态,然后计算出对于每一个\(i \in [l,r ...

  6. 洛谷 P3975 / loj 2102 [TJOI2015] 弦论 题解【后缀自动机】【拓扑排序】

    后缀自动机入门. 题目描述 为了提高智商,ZJY 开始学习弦论. 这一天,她在<String theory>中看到了这样一道问题:对于一个给定的长度为 \(n\) 的字符串,求出它的第 \ ...

  7. 【后缀自动机】【拓扑排序】【动态规划】hihocoder1457 后缀自动机四·重复旋律7

    解题方法提示 小Hi:我们已经学习了后缀自动机,今天我们再来看这道有意思的题. 小Ho:好!这道题目让我们求的是若干的数字串所有不同子串的和. 小Hi:你能不能结合后缀自动机的性质来思考如何解决本题? ...

  8. LOJ3049 [十二省联考2019] 字符串问题 【后缀自动机】【倍增】【拓扑排序】

    题目分析: 建出后缀自动机,然后把A串用倍增定位到后缀自动机上,再把B串用倍增定位到后缀自动机上. SAM上每个点上的A串根据长度从小到大排序,建点,依次连边. 再对于SAM上面每个点,连到儿子的边, ...

  9. Luogu5284 十二省联考2019字符串问题(后缀树+拓扑排序)

    对反串建SAM弄出后缀树,每个b串通过倍增定位其在后缀树上对应的节点,根据其长度将节点拆开.然后每个a串也找到对应的节点,由该节点向表示a串的节点连边,再把所给的边连上跑拓扑排序即可. #includ ...

随机推荐

  1. Mybatis入门之动态sql

    Mybatis入门之动态sql 通过mybatis提供的各种标签方法实现动态拼接sql. 1.if.where.sql.include标签(条件.sql片段) <sql id="sel ...

  2. Vue 无限滚动加载指令

    也不存在什么加载咯, 就是一个判断滚动条是否到达浏览器底部了. 如果到了就触发事件,米到就不处理. 计算公式提简单的   底部等于(0) =  滚动条高度 - 滚动条顶部距离 - 可视高度.  反正结 ...

  3. python迭代器与生成器及yield

    一.迭代器(itertor) 1.可迭代: 在Python中如果一个对象有__iter__()方法或__getitem__()方法,则称这个对象是可迭代的(iterable). 其中__iter__( ...

  4. Java实现"命令式"简易文本编辑器原型

    源自早先想法, 打算从界面方向做些尝试. 找到个简单文本编辑器的实现: Simple Text Editor - Java Tutorials. 原本的菜单/按钮界面如下. 包括基本功能: 新建/打开 ...

  5. Nginx Windows详细安装部署教程

    一.Nginx简介 Nginx (engine x) 是一个高性能的HTTP和反向代理服务器,也是一个IMAP/POP3/SMTP服务器.Nginx是由伊戈尔·赛索耶夫为俄罗斯访问量第二的Ramble ...

  6. 智能指针std::unique_ptr

    std::unique_ptr 1.特性 1) 任意时刻只能由一个unique_ptr指向某个对象,指针销毁时,指向的对象也会被删除(通过内置删除器,通过调用析构函数实现删除对象) 2)禁止拷贝和赋值 ...

  7. VSCode 下载Models 报错

    VSCode调试部分代码时,报错,提示不能自动获取Models.报错信息如下. go: golang.org/x/crypto@v0.-80db560fac1f: unrecognized impor ...

  8. Redis与Memocache的区别

    转载地址:http://gnucto.blog.51cto.com/3391516/998509 Redis与Memcached的区别 传统MySQL+ Memcached架构遇到的问题 实际MySQ ...

  9. js 学习之路9:运算符

    1. 算数运算符 运算符 描述 例子 结果 + 加 x=y+2 x=7 - 减 x=y-2 x=3 * 乘 x=y*2 x=10 / 除 x=y/2 x=2.5 % 求余数 (保留整数) x=y%2 ...

  10. Win 10 和 Ubuntu 16.04 双系统,安装完成后,设置默认的启动项

    当安装好了 Windows 和 Ubuntu 双系统之后,默认的启动项是 Ubuntu,我们可以来设置默认的启动项, 开机时,在启动项选择处,可以通过↑↓ 键来选择启动哪个系统,第一行序号是 0 ,第 ...