传送门: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. Databricks 第6篇:Spark SQL 维护数据库和表

    Spark SQL 表的命名方式是db_name.table_name,只有数据库名称和数据表名称.如果没有指定db_name而直接引用table_name,实际上是引用default 数据库下的表. ...

  2. linux + svn提交日志不能显示 日期一直都是1970-01-01

    网上很多都是说将svn安装目录下的svnserve.conf文件中的anon-access 设置为read,但是 经查阅并测试, 设置为: anon-access = none  是正确的,设置成 r ...

  3. 深入理解MySQL索引(上)

    简单来说,索引的出现就是为了提高数据查询的效率,就像字典的目录一样.如果你想快速找一个不认识的字,在不借助目录的情况下,那我估计你的找好长时间.索引其实就相当于目录. 几种常见的索引模型 索引的出现是 ...

  4. PAT天梯赛练习 L3-004 肿瘤诊断 (30分) 三维BFS

    题目分析: 可能是我的理解能力比较差,在读题的时候一直以为所有的切片是可以排列组合的,并不是按照输入顺序就定死的,那么这题就变得十分的复杂啦~~~~~,查看的题解之后发现所有的切片并没有所谓的自由组合 ...

  5. wpf 在不同DPI下如何在DrawingVisual中画出清晰的图形

    环境Win10 VS2017 .Net Framework4.7.1   本文仅讨论在DrawingVisual中进行的画图.   WPF单位,系统DPI,显示器DPI三者的定义及关系 WPF单位:一 ...

  6. 使用sqluldr2进行oracle数据库抽取时执行后无反应,也无日志

    使用sqluldr2进行oracle数据库表数据抽取时遇到执行后无反应,也不报错,也无日志输出的情况. 经过排查之后发现时由于oracle账户密码快要过期导致的(这也能出问题,我服,类似的plsql连 ...

  7. Empire

    Empire 内网渗透神器 一 基本渗透 安装 git clone https://github.com/BC-SECURITY/Empire/ ./setup/install.sh 启动 ./emp ...

  8. 【转】自定义ALV控件的工具条按钮

    1 CLASS lcl_event_receiver DEFINITION DEFERRED. 2 3 DATA: itab TYPE TABLE OF spfli, 4 wa TYPE spfli. ...

  9. springAOP的概述及使用

    Spring AOP SpringAOP是Spring中非常重要的功能模块之一,该模块提供了面向切面编程,在事务处理,日志记录,安全控制等操作中广泛使用. SpringAOP的基本概念 AOP的概念 ...

  10. 如何在 Blazor WebAssembly中 使用 功能开关

    微软Azure 团队开发的 功能管理 (Feature Management) 包 Microsoft.FeatureManagement可用于实现 功能开关,可以通过 功能开关 特性动态的改变应用程 ...