传送门: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. LeetCode448-数组中消失的数字

    题目 给定一个范围在 1 ≤ a[i] ≤ n ( n = 数组大小 ) 的 整型数组,数组中的元素一些出现了两次,另一些只出现一次. 找到所有在 [1, n] 范围之间没有出现在数组中的数字. 您能 ...

  2. 剑指offer-56数组中数字出现的次数

    题目 一个整型数组 nums 里除两个数字之外,其他数字都出现了两次.请写程序找出这两个只出现一次的数字.要求时间复杂度是O(n),空间复杂度是O(1). 输入:nums = [4,1,4,6] 输出 ...

  3. python中re模块的使用(正则表达式)

    一.什么是正则表达式? 正则表达式,又称规则表达式,通常被用来检索.替换那些符合某个模式(规则)的文本. 正则表达式是对字符串操作的一种逻辑公式,就是用事先定义好的一些特定字符.及这些特定字符的组合, ...

  4. 创建一个简单MyBatis程序

    文章目录 MyBatis基础 MyBatis 简介 创建一个MyBatis程序 1. 创建Java项目 2. 加载MyBatis包 3. 编写POJO类和映射文件 4.创建mybatis-config ...

  5. vagrant up报错【io.rb:32:in `encode': "\x95" followed by "\"" on GBK (Encoding::InvalidByteSequenceError)】

    vagrant up报错[io.rb:32:in `encode': "\x95" followed by """ on GBK (Encoding: ...

  6. 使用Canal作为mysql的数据同步工具

    一.Canal介绍 1.应用场景 在前面的统计分析功能中,我们采取了服务调用获取统计数据,这样耦合度高,效率相对较低,目前我采取另一种实现方式,通过实时同步数据库表的方式实现,例如我们要统计每天注册与 ...

  7. codeup 1934 查找元素

    题目描述: 输入一个数n,然后输入n个数值各不相同,再输入一个值x,输出这个值在这个数组中的下标(从0开始,若不在数组中则输出-1. 输入: 测试数据有多组,输入n(1<=n<=200), ...

  8. spring boot 启动 开启注解 加载 bean

    业务描述:创建一个cache类然后交给spring 管理. @Component @Scope("singleton") public class Cache { public C ...

  9. P5858 Golden Swold

    写在前面 简单的单调队列优化 DP 处理略微有点恶心,于是乎,用来取 \(\max\) 的极小值直接开到了 long long 的最小极限,了 define int long long /cy 算法思 ...

  10. qbxt 学习笔记 10.2

    写在前面 昨晚网络咕了,而且比较晚,没交作业.解题报告写成书面的了,代码另发 + 博客. 目录 写在前面 1. 爬山算法 概述 示例 2. 模拟退火 概述 3. Meet in the Middle ...