BZOJ5261 Rhyme--广义SAM+拓扑排序
原题链接,不是权限题
题目大意
有\(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+拓扑排序的更多相关文章
- BZOJ2938 [Poi2000]病毒 和 BZOJ5261 Rhyme
[Poi2000]病毒 二进制病毒审查委员会最近发现了如下的规律:某些确定的二进制串是病毒的代码.如果某段代码中不存在任何一段病毒代码,那么我们就称这段代码是安全的.现在委员会已经找出了所有的病毒代码 ...
- CodeForces - 666E: Forensic Examination (广义SAM 线段树合并)
题意:给定字符串S,然后M个字符串T.Q次询问,每次给出(L,R,l,r),问S[l,r]在L到R这些T字符串中,在哪个串出现最多,以及次数. 思路:把所有串建立SAM,然后可以通过倍增走到[l,r] ...
- 【BZOJ 3473】 字符串 (后缀数组+RMQ+二分 | 广义SAM)
3473: 字符串 Description 给定n个字符串,询问每个字符串有多少子串(不包括空串)是所有n个字符串中至少k个字符串的子串? Input 第一行两个整数n,k. 接下来n行每行一个字符串 ...
- Luogu P3181 [HAOI2016]找相同字符 广义$SAM$
题目链接 \(Click\) \(Here\) 设一个串\(s\)在\(A\)中出现\(cnt[s][1]\)次,在\(B\)中出现\(cnt[s][2]\)次,我们要求的就是: \[\sum cnt ...
- CF666E Forensic Examination 广义SAM、线段树合并、倍增、扫描线
传送门 朴素想法:对\(M\)个匹配串\(T_1,...,T_M\)建立广义SAM,对于每一次询问,找到这个SAM上\(S[pl...pr]\)对应的状态,然后计算出对于每一个\(i \in [l,r ...
- 洛谷 P3975 / loj 2102 [TJOI2015] 弦论 题解【后缀自动机】【拓扑排序】
后缀自动机入门. 题目描述 为了提高智商,ZJY 开始学习弦论. 这一天,她在<String theory>中看到了这样一道问题:对于一个给定的长度为 \(n\) 的字符串,求出它的第 \ ...
- 【后缀自动机】【拓扑排序】【动态规划】hihocoder1457 后缀自动机四·重复旋律7
解题方法提示 小Hi:我们已经学习了后缀自动机,今天我们再来看这道有意思的题. 小Ho:好!这道题目让我们求的是若干的数字串所有不同子串的和. 小Hi:你能不能结合后缀自动机的性质来思考如何解决本题? ...
- LOJ3049 [十二省联考2019] 字符串问题 【后缀自动机】【倍增】【拓扑排序】
题目分析: 建出后缀自动机,然后把A串用倍增定位到后缀自动机上,再把B串用倍增定位到后缀自动机上. SAM上每个点上的A串根据长度从小到大排序,建点,依次连边. 再对于SAM上面每个点,连到儿子的边, ...
- Luogu5284 十二省联考2019字符串问题(后缀树+拓扑排序)
对反串建SAM弄出后缀树,每个b串通过倍增定位其在后缀树上对应的节点,根据其长度将节点拆开.然后每个a串也找到对应的节点,由该节点向表示a串的节点连边,再把所给的边连上跑拓扑排序即可. #includ ...
随机推荐
- PHP设计模式的六大设计原则
PHP设计模式的六大设计原则 1 简介 软件设计最大的难题就是应对需求的变化,但是纷繁复杂的需求变化却是不可预料的.此时,我们可以通过六大设计原则良好的应对未来的变化. 2 讲解 2.1 单一职责原则 ...
- SAP HUM 拆包之后的HU号码依旧存在
比如HU 194811210666,已经被执行了Unpack操作. HUMO看这个HU号码, 执行, 可以发现,这个HU是空的,里面什么都没有. 双击HU号码,进入HU显示界面, 系统状态竟然是PHE ...
- 【设计模式】单例模式 Singleton Pattern
通常我们在写程序的时候会碰到一个类只允许在整个系统中只存在一个实例(Instance) 的情况, 比如说我们想做一计数器,统计某些接口调用的次数,通常我们的数据库连接也是只期望有一个实例.Windo ...
- Snapde和Excel、PowerPivot、WPS打开超大CSV文件性能比较
Snapde,一个专门为编辑超大型数据量CSV文件而设计的单机版电子表格软件:它运行的速度非常快,反应非常灵敏. 我们拿四份文件进行测试,对比一下他们打开大文件的性能:loan_theme_ids.c ...
- Android应用跳转到微信和支付宝扫一扫
新版的微信已经把微信功能的schema都禁掉了,意味着我们无法打开微信的扫一扫等功能,目前正常的只能先进入微信(我测试的时候是微信版本7.0.3)已经是很新的版本了 具体调起微信扫一扫代码如下,测试后 ...
- Android设计模式总结
1.复合模式:三层架构.MVC.MVP.MVVM 2.设计模式-单例模式 配置类的使用. 3.设计模式-模板方法 通过抽象类或接口提前定义要实现的方法. 4.设计模式-观察者模式 消息的通知. 5.设 ...
- 逻辑回归&线性支持向量机
代码: # -*- coding: utf-8 -*- """ Created on Tue Jul 17 10:13:20 2018 @author: zhen &qu ...
- C# Dictionary 函数解析及使用方法
要使用Dictionary集合,需要导入C#泛型命名空间 System.Collections.Generic(程序集:mscorlib) Dictionary的描述 1.从一组键(Key)到一组值 ...
- C# 当前 .NET SDK 不支持将 .NET Core 2.1 设置为目标。请将 .NET Core 2.0 或更低版本设置为目标,或使用支持 .NET Core 2.1 的 .NET SDK 版本。
报错信息: 严重性 代码 说明 项目 文件 行 禁止显示状态 错误 NETSDK1045 当前 .NET SDK 不支持将 .NET Core 2.2 设置为目标.请将 .NET Core 2.1 或 ...
- 记一次 c 语言 的 多线程查找 简单实现
//仅供参考学习 1 #define _CRT_SECURE_NO_WARNINGS //屏蔽 vs 的a #include <stdio.h> #include <stdlib.h ...