传送门:Easygoing Single Tune Circulation

题意

给定n个字符串 s[i],再给出m个查询的字符串 t[i],问 t[i] 是否为某个 s[i] 循环无限次的子串。

题解

分成两种情况

① t[i] 比 s[j] 短, 这个时候可以用后缀自动机,把每个 s[j] 重复一次,然后放到SAM中,这样直接每次直接查询就好了。当然,因为是有t(t<=1e5)组数据,全部初始化肯定会超时的,所以下一个要用到哪部分哪部分就初始化,这样最多初始化1e6次,因为所有的长度不会超过1e6。

② t[i] 比 s[j] 长,这样的话就不能用后缀自动机了,因为并不知道 t[i] 到底是 s[j] 重复了几次的子串。考虑到 t[i] 如果是某个 s[j] 循环无限次的子串,那么他一定是有循环节的,因为不知道有没有循环节,所以找到第一个最长的且每个字母只出现了一次的子串,并用最小表示法表示,然后在一颗插入了每个 s[j] 的最小表示法的字典树中查找是否有这个子串,如果有的话,构造长度为 |t[i]|,以该子串为循环节的串,如果这个串就是 t[i] 那么说明有该串,否则没有。当然,字典树的初始化也会卡,所以同样的边用边初始化下一个即将要用的。

代码

  1 #include<bits/stdc++.h>
2 using namespace std;
3
4 const int maxn=1e6+10;
5
6 struct SAM
7 {
8 int mxlen[maxn<<1],link[maxn<<1],nt[maxn<<1][26];
9 int sz,last,len;
10 void init()
11 {
12 memset(nt[0],0,sizeof(nt[0]));
13 mxlen[0]=0;
14 link[0]=-1;
15 sz=1;
16 last=0;
17 }
18
19 void extend(int c)
20 {
21 c-='a';
22 int cur=sz++;
23 mxlen[cur]=mxlen[last]+1;
24 int p=last;
25 memset(nt[cur],0,sizeof(nt[cur]));
26 while(p!=-1&&!nt[p][c]){
27 nt[p][c]=cur;
28 p=link[p];
29 }
30 if(p==-1) link[cur]=0;
31 else{
32 int q=nt[p][c];
33 if(mxlen[p]+1==mxlen[q]) link[cur]=q;
34 else{
35 int clone=sz++;
36 mxlen[clone]=mxlen[p]+1;
37 memcpy(nt[clone],nt[q],sizeof(nt[q]));
38 link[clone]=link[q];
39 while(p!=-1&&nt[p][c]==q){
40 nt[p][c]=clone;
41 p=link[p];
42 }
43 link[q]=link[cur]=clone;
44 }
45 }
46 last=cur;
47 }
48 bool query(string s)
49 {
50 int p=0;
51 for(int i=0;i<s.size();i++){
52 int c=s[i]-'a';
53 if(!nt[p][c]) return 0;
54 p=nt[p][c];
55 }
56 return 1;
57 }
58 }sam;
59
60 struct Trie
61 {
62 int tree[maxn][30];
63 int color[maxn];
64 int k=1;
65
66 int newnode()
67 {
68 memset(tree[k],0,sizeof(tree[k]));
69 color[k]=0;
70 return k++;
71 }
72
73 void init()
74 {
75 color[0]=0;
76 memset(tree[0],0,sizeof(tree[0]));
77 k=1;
78 }
79
80 void insert(string a)
81 {
82 int p=0;
83 int len=a.size();
84 for(int i=0;i<len;i++){
85 int c=a[i]-'a';
86 if(!tree[p][c]) {
87 tree[p][c]=newnode();
88 }
89 p=tree[p][c];
90 }
91 color[p]++;
92 }
93 int query(string a)
94 {
95 int p=0;
96 int len=a.size();
97 for(int i=0;i<len;i++){
98 int c=a[i]-'a';
99 if(!tree[p][c]) return 0;
100 p=tree[p][c];
101 }
102 return color[p];
103 }
104 }trie;
105
106 const int maxm=1e5+10;
107 string s[maxm];
108
109 string get_min(string s)
110 {
111 string t;
112 int p=26,pos=0;
113 for(int i=0;i<s.size();i++){
114 if(s[i]-'a'<=p) p=s[i]-'a',pos=i;
115 }
116 t=s.substr(pos);
117 t+=s.substr(0,pos);
118 return t;
119 }
120
121 int main()
122 {
123 ios::sync_with_stdio(false);
124 cin.tie(0);
125 cout.tie(0);
126 int T;
127 cin>>T;
128 int k=1;
129 while(T--){
130 sam.init();
131 trie.init();
132 cout<<"Case #"<<k++<<":"<<endl;
133 int n;
134 cin>>n;
135 for(int i=0;i<n;i++){
136 cin>>s[i];
137 string t=get_min(s[i]);
138 trie.insert(t);
139 }
140 for(int i=0;i<n;i++){
141 sam.last=0;
142 for(int j=0;j<s[i].size();j++) sam.extend(s[i][j]);
143 for(int j=0;j<s[i].size();j++) sam.extend(s[i][j]);
144 }
145 int m;
146 cin>>m;
147 while(m--){
148 string a;
149 cin>>a;
150 if(sam.query(a)){
151 cout<<"YES"<<endl;
152 continue;
153 }
154 int pos=a.size();
155 for(int i=1;i<pos;i++){
156 if(a[i]==a[0]){
157 pos=i;
158 break;
159 }
160 }
161 string t=a.substr(0,pos);
162 string tt=get_min(t);
163 if(trie.query(tt)){
164 string p;
165 for(int i=0;i<a.size();i++){
166 p+=t[i%t.size()];
167 }
168 if(p==a){
169 cout<<"YES"<<endl;
170 continue;
171 }
172 }
173 cout<<"NO"<<endl;
174 }
175 }
176 return 0;
177 }

牛客15334 Easygoing Single Tune Circulation(后缀自动机+字典树)的更多相关文章

  1. BZOJ3413: 匹配(后缀自动机 线段树合并)

    题意 题目链接 Sol 神仙题Orz 后缀自动机 + 线段树合并... 首先可以转化一下模型(想不到qwq):问题可以转化为统计\(B\)中每个前缀在\(A\)中出现的次数.(画一画就出来了) 然后直 ...

  2. cf666E. Forensic Examination(广义后缀自动机 线段树合并)

    题意 题目链接 Sol 神仙题Orz 后缀自动机 + 线段树合并 首先对所有的\(t_i\)建个广义后缀自动机,这样可以得到所有子串信息. 考虑把询问离线,然后把\(S\)拿到自动机上跑,同时维护一下 ...

  3. 洛谷P2178 [NOI2015]品酒大会(后缀自动机 线段树)

    题意 题目链接 Sol 说一个后缀自动机+线段树的无脑做法 首先建出SAM,然后对parent树进行dp,维护最大次大值,最小次小值 显然一个串能更新答案的区间是\([len_{fa_{x}} + 1 ...

  4. BZOJ1396: 识别子串(后缀自动机 线段树)

    题意 题目链接 Sol 后缀自动机+线段树 还是考虑通过每个前缀的后缀更新答案,首先出现次数只有一次,说明只有\(right\)集合大小为\(1\)的状态能对答案产生影响 设其结束位置为\(t\),代 ...

  5. [Luogu5161]WD与数列(后缀数组/后缀自动机+线段树合并)

    https://blog.csdn.net/WAautomaton/article/details/85057257 解法一:后缀数组 显然将原数组差分后答案就是所有不相交不相邻重复子串个数+n*(n ...

  6. 洛谷P4493 [HAOI2018]字串覆盖(后缀自动机+线段树+倍增)

    题面 传送门 题解 字符串就硬是要和数据结构结合在一起么--\(loj\)上\(rk1\)好像码了\(10k\)的样子-- 我们设\(L=r-l+1\) 首先可以发现对于\(T\)串一定是从左到右,能 ...

  7. luogu5212/bzoj2555 substring(后缀自动机+动态树)

    对字符串构建一个后缀自动机. 每次查询的就是在转移边上得到节点的parent树中后缀节点数量. 由于强制在线,可以用动态树维护后缀自动机parent树的子树和. 注意一个玄学的优化:每次在执行连边操作 ...

  8. 模板—字符串—后缀自动机(后缀自动机+线段树合并求right集合)

    模板—字符串—后缀自动机(后缀自动机+线段树合并求right集合) Code: #include <bits/stdc++.h> using namespace std; #define ...

  9. 【BZOJ4556】[TJOI2016&HEOI2016] 字符串(后缀自动机+线段树合并+二分)

    点此看题面 大致题意: 给你一个字符串\(s\),每次问你一个子串\(s[a..b]\)的所有子串和\(s[c..d]\)的最长公共前缀. 二分 首先我们可以发现一个简单性质,即要求最长公共前缀,则我 ...

随机推荐

  1. .NET 云原生架构师训练营(模块二 基础巩固 敏捷开发)--学习笔记

    2.7.1 敏捷开发 敏捷介绍 敏捷的起源 敏捷软件开发宣言 敏捷开发十二原则 生命周期对比 敏捷开发的特点 敏捷的发展 敏捷的核心 敏捷的起源 2001年,17个老头子在一起一边滑雪,一边讨论工作, ...

  2. Linux学习笔记 | docker基本命令

    Docker的三大核心概念:镜像.容器.仓库 镜像:类似虚拟机的镜像.用俗话说就是安装文件. 容器:类似一个轻量级的沙箱,容器是从镜像创建应用运行实例,可以将其启动.开始.停止.删除.而这些容器都是相 ...

  3. MySQL select join on 连表查询和自连接查询

    连表查询 JOIN ON 操作 描述 inner join 只返回匹配的值 right join 会从右表中返回所有的值, 即使左表中没有匹配 left join 会从左表中返回所有的值, 即使右表中 ...

  4. (十九)hashlib模块

    hashlib模块用于加密相关的操作,3.x里代替了md5模块和sha模块,主要提供 SHA1, SHA224, SHA256, SHA384, SHA512 ,MD5 算法 注意:md5和sha25 ...

  5. (十一)time模块

    三种时间表示 在Python中,通常有这几种方式来表示时间: 时间戳(timestamp) :通常来说,时间戳表示的是从1970年1月1日00:00:00开始按秒计算的偏移量.我们运行"ty ...

  6. 【EXP】比较大的dmp文件导入的时候可以将界面关掉

    有一个需求,将一个dmp文件导入到数据库中,但是这个crt的回话有timeout,3分钟,所以一到三分钟就掉线,导入就失败了,这让人很头疼,关闭界面,imp也就终止了 最后想到了几招,分享一下: 1. ...

  7. Goby资产扫描工具安装及报错处理

    官网: https://cn.gobies.org/index.html 产品介绍: 帮企业梳理资产暴露攻击面,新一代网络安全技术,通过为目标建立完整的资产数据库,实现快速的安全应急. 已有功能: 扫 ...

  8. 关于BAPI_TRANSACTION_COMMIT一点说明

    我们调用bapi做了相关的业务操作后,通常都要在后面调用 BAPI_TRANSACTION_COMMIT来提交所做得更改 然而,有时候,在程序中需要调用多个不同的BAPI实现不同的功能,那么这个时候就 ...

  9. 1.5V升压3V集成电路升压芯片

    干电池1.5V升压3V的升压芯片,适用于干电池升压产品输出3V供电 1.5V输入时,输出3V,电流可达500MA. PW5100是一款效率大.10uA低功耗 PW5100输入电压:0.7V-5V PW ...

  10. tf

    第2章 Tensorflow keras实战 2-0 写在课程之前 课程代码的Tensorflow版本 大部分代码是tensorflow2.0的 课程以tf.kerasAPI为主,因而部分代码可以在t ...