原题链接,不是权限题

题目大意

有\(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. 设计模式系列6:适配器模式(Adapter Pattern)

    定义 将一个类的接口转换成客户希望的另一个接口,适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作.    --<设计模式>GoF UML类图 使用场景 在遗留代码复用,类 ...

  2. 倒计时5S秒自动关闭弹窗

    <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...

  3. EF时,数据库字段和实体类不一致问题

    场景:由于一些原因,实体中属性比数据库中字段多了一个startPage属性.PS:controllers中用实体类去接收参数,但是传入的参数比数据库中实体表多了一个字段, 这种情况下,应该建一个vie ...

  4. MySQL-每日定点运行

    最近做项目的时候设计每天定点执行的脚本,所以在这 马克一下,方便查找 set time_zone = '+8:00';      set GLOBAL event_scheduler = 1;     ...

  5. Mybatis学习---连接MySQL数据库

    [目录]

  6. 深圳市共创力推出独家课程《AHB和OSG》高级实务培训课程!

     <AHB和OSG>高级实务培训课程大纲 [适合对象]:高层管理者.产品经理.资源经理.各专项经理.研发等部门的负责人和骨干员工. [课程受益]:高层如何对项目的优先级进行排序和资源分配. ...

  7. 第五篇Scrum冲刺博客

    一.Daily Scrum Meeting照片 二.每个人的工作 成员 ItemID 已完成工作 明天计划完成的工作 遇到的困难 张鸿 o1 整合界面至游戏中 将其他剩余功能进行整合 游戏状态的切换 ...

  8. liteos简介(一)

    LiteOS是在2015华为网络大会上华为发布的敏捷网络3.0中的一个轻量级的物联网操作系统,LiteOS体积只有10KB级. 在Hi3559A中,liteos是用于Cortex-A53,用于处理MP ...

  9. Windows -- 从注册表删除IE浏览器加载项

    Windows -- 从注册表删除IE浏览器加载项 1.  一部分加载项从注册表以下位置直接删除 2.  一部分扩展项从注册表以下位置直接删除

  10. C# -- 使用 Task 执行多线程任务

    C# -- 使用 Task 执行多线程任务 1. 使用 Task 执行多线程任务 class Program { static void Main(string[] args) { Task task ...