Codeforces 235C Cyclical Quest - 后缀自动机
Some days ago, WJMZBMR learned how to answer the query "how many times does a string x occur in a string s" quickly by preprocessing the string s. But now he wants to make it harder.
So he wants to ask "how many consecutive substrings of s are cyclical isomorphic to a given string x". You are given string s and n strings xi, for each string xi find, how many consecutive substrings of s are cyclical isomorphic to xi.
Two strings are called cyclical isomorphic if one can rotate one string to get the other one. 'Rotate' here means 'to take some consecutive chars (maybe none) from the beginning of a string and put them back at the end of the string in the same order'. For example, string "abcde" can be rotated to string "deabc". We can take characters "abc" from the beginning and put them at the end of "de".
The first line contains a non-empty string s. The length of string s is not greater than 106 characters.
The second line contains an integer n (1 ≤ n ≤ 105) — the number of queries. Then n lines follow: the i-th line contains the string xi — the string for the i-th query. The total length of xi is less than or equal to 106 characters.
In this problem, strings only consist of lowercase English letters.
For each query xi print a single integer that shows how many consecutive substrings of s are cyclical isomorphic to xi. Print the answers to the queries in the order they are given in the input.
baabaabaaa 5 a ba baa aabaa aaba
7 5 7 3 5
aabbaa 3 aa aabb abba
2 3 3
题目大意 给定一个字符串s,和一堆字符串x,问每个字符串x和多少个s的子串循环同构。字符串s和字符串t循环同构是指,将s的某个前缀(可以是空串)挪到s的尾部使得和字符串t相等。
显然后缀自动机
先对s建立后缀自动机。然后对于每个不是为了解决分割状态的状态的cnt设为1,接着从parent树的叶节点开始向上累加cnt。
对于每个询问的x,我们将x的除去最后一个字符的串复制一份,塞进x的末尾。然后扔进s的后缀自动机匹配LCS,当发现当前匹配的长度大于等于串x原本的长度,然后就开始跳par指针(但是我比较喜欢写fail),直到当前匹配的长度为串x原本长度的子串的长度存在当前状态的长度区间内,然后累计答案。
由于这么做会重复一些东西,比如输入的串像什么"aaaaaaaa",显然就会重复计数。所以你需要给每个节点记录一个时间戳来判断是否被当前询问统计过。
我最开始的想法是,对于两个循环同构的串,显然可以保留其中某一个的后缀然后把剩下的部分挪到后面去使得两个串相等。
后缀自动机中跳par指针,实质上是正在访问某个子串的后缀。
所以考虑如果len - 1不在当前的长度区间内,就往回跳一步去匹配下一个字符(指前面的字符转上来),假设直接到达下一个字符,然后累加答案,更新时间戳。
如果过程不是很顺利,发生了失配,那么又变成了一个当前串的后缀,于是,需要把舍弃的部分加上来,然后继续去for。
简单地说就是设法线性地实现循环同构的匹配。
(后来我想了想,发现这两种做法本质是相同的。)
Code
/**
* Codeforces
* Problem#235C
* Accepted
* Time:514ms
* Memory:278100k
*/
#include <bits/stdc++.h>
using namespace std; #define charset 26
//这是一个膜拜大佬的宏定义
#define ModYJQ 2333333 inline int cti(char x) { return x - 'a'; } typedef class TrieNode {
public:
TrieNode* next[charset];
TrieNode* fail;
vector<TrieNode*> son;
int cnt;
int len;
int t;
TrieNode():fail(NULL), cnt(), len(), t(ModYJQ) {
memset(next, , sizeof(next));
}
}TrieNode; typedef class SuffixAutomachine {
public:
TrieNode *pool;
TrieNode *top; TrieNode* root;
TrieNode* last; TrieNode* newnode() {
return top++;
} TrieNode* newnode(int len) {
top->len = len;
return top++;
} SuffixAutomachine():pool(NULL), top(NULL), root(NULL), last(NULL) { }
SuffixAutomachine(int len) {
pool = new TrieNode[len * + ];
top = pool;
root = newnode();
last = root;
} inline void extend(char x) {
int c = cti(x);
TrieNode* node = newnode(last->len + ), *f = last;
node->cnt = ;
while(f && f->next[c] == NULL)
f->next[c] = node, f = f->fail;
if(f == NULL) node->fail = root;
else {
TrieNode *p = f->next[c];
if(p->len == f->len + ) node->fail = p;
else {
TrieNode *clone = newnode(f->len + );
memcpy(clone->next, p->next, sizeof(clone->next));
clone->fail = p->fail;
p->fail = clone;
node->fail = clone;
while(f && f->next[c] == p)
f->next[c] = clone, f = f->fail;
}
}
last = last->next[c];
}
}SuffixAutomachine; int lens;
int n;
char str[];
SuffixAutomachine sam; inline void init() {
gets(str);
lens = strlen(str);
} int dfs(TrieNode* p) {
for(int i = ; i < (signed)p->son.size(); i++)
p->cnt += dfs(p->son[i]);
return p->cnt;
} inline void init_sam() {
sam = SuffixAutomachine(lens);
for(int i = ; i < lens; i++)
sam.extend(str[i]);
for(TrieNode* p = sam.pool; p != sam.top; p++) {
if(p->fail)
p->fail->son.push_back(p);
}
dfs(sam.root);
} inline void solve() {
scanf("%d", &n);
gets(str);
while(n--) {
gets(str);
lens = strlen(str);
memcpy(str + lens, str, sizeof(char) * (lens - ));
TrieNode* p = sam.root, *q;
int res = ;
for(int i = , nlen = , c; i < * lens - && p; i++) {
c = cti(str[i]);
if(!p->next[c]) {
while(p && !p->next[c]) p = p->fail, nlen = (p) ? (p->len) : ();
if(!p) break;
}
p = p->next[c], nlen++;
// printf("%d %d\n", lens, nlen);
if(nlen >= lens) {
q = p;
while(q->fail->len >= lens)
q = q->fail;
if(q->t != n) {
res += q->cnt;
q->t = n;
}
}
}
printf("%d\n", res);
}
} int main() {
init();
init_sam();
solve();
return ;
}
Codeforces 235C Cyclical Quest - 后缀自动机的更多相关文章
- CF 235C. Cyclical Quest [后缀自动机]
题意:给一个主串和多个询问串,求询问串的所有样子不同的周期同构出现次数和 没有周期同构很简单就是询问串出现次数,|Right| 有了周期同构,就是所有循环,把询问串复制一遍贴到后面啊!思想和POJ15 ...
- 【Codeforces235C】Cyclical Quest 后缀自动机
C. Cyclical Quest time limit per test:3 seconds memory limit per test:512 megabytes input:standard i ...
- Codeforces 235C. Cyclical Quest
传送门 写的时候挺蛋疼的. 刚开始的时候思路没跑偏,无非就是建个SAM然后把串开两倍然后在SAM上跑完后统计贡献.但是卡在第二个样例上就是没考虑相同的情况. 然后开始乱搞,发现会出现相同串的只有可能是 ...
- CodeForces 235C Cyclical Quest(后缀自动机)
[题目链接] http://codeforces.com/contest/235/problem/C [题目大意] 给出一个字符串,给出一些子串,问每个子串分别在母串中圆环匹配的次数,圆环匹配的意思是 ...
- Codeforces Round #146 (Div. 1) C - Cyclical Quest 后缀自动机+最小循环节
#include<bits/stdc++.h> #define LL long long #define fi first #define se second #define mk mak ...
- Codeforces 235C Cyclical Quest 字符串 SAM KMP
原文链接https://www.cnblogs.com/zhouzhendong/p/CF235C.html 题目传送门 - CF235C 题意 给定一个字符串 $s$ ,多组询问,每组询问的形式为 ...
- Codeforces 452E Three Strings(后缀自动机)
上学期很认真地学了一些字符串的常用工具,各种 suffix structre,但是其实对后缀自动机这个部分是理解地不太透彻的,以致于看了师兄A这题的代码后,我完全看不懂,于是乎重新看回一些学习后缀自动 ...
- Codeforces.700E.Cool Slogans(后缀自动机 线段树合并 DP)
题目链接 \(Description\) 给定一个字符串\(s[1]\).一个字符串序列\(s[\ ]\)满足\(s[i]\)至少在\(s[i-1]\)中出现过两次(\(i\geq 2\)).求最大的 ...
- 后缀自动机(SAM)
*在学习后缀自动机之前需要熟练掌握WA自动机.RE自动机与TLE自动机* 什么是后缀自动机 后缀自动机 Suffix Automaton (SAM) 是一个用 O(n) 的复杂度构造,能够接受一个字符 ...
随机推荐
- cocos2d-x JS 四人麻将中的服务器位置与客户端位置转换相关
前言:在写各类游戏编程中,都会遇到一个问题,就是位置问题,服务端的位置是与客户端的位置是不同的,这中间需要进行一个转化,客户端一套代码运行,不管是任何人登陆,该位置始终都是在屏幕正下方,所以这样就要进 ...
- C++调用openssl库生成RSA加密秘钥对
直接上代码.默认生成的是pkcs#1格式 // ---- rsa非对称加解密 ---- // #define KEY_LENGTH 1024 // 密钥长度 #define PUB_KEY_FILE ...
- ssh整合not found class 异常总结
(1)org.apache.tomcat.dbcp.dbcp.SQLNestedException: Cannot load JDBC driver class 'com.microsoft.sqls ...
- SQL中的replace函数
REPLACE 用第三个表达式替换第一个字符串表达式中出现的所有第二个给定字符串表达式. 语法 REPLACE ( 'string_expression1' , 'string_expression2 ...
- 关于Python veriable scope 的一点疑问
在写程序中遇到了类似于以下代码的问题: #不会报错 a=1 def f(): print(a) f() #会报错 a=1 def f(): a+=1 f()
- python:基于tkinter的定时关机程
本人使用python3 from tkinter import* import os from PIL import Image, ImageTk root=Tk() a=Label(root,tex ...
- uva11990 动态逆序对
这题说的是给了一个数组,按照他给的顺序依次删除数,在删除之前输出此时的逆序对个数 我们用Fenwick树 维护这整个数列, C[i]是一个 treap的头, 管理了在树状数组中 能影响他的点,然后我们 ...
- hdu2295DLX重复覆盖+二分
题目是说 给了n个城市 m个雷达 你只能选择其中的k个雷达进行使用 你可以设置每个雷达的半径,最后使得所有城市都被覆盖,要求雷达的半径尽可能的小(所有雷达的半径是一样的) 二分最小半径,然后每次重新建 ...
- cocos v3.10 下载地址
官方给出的是在:http://www.cocos2d-x.org/filedown/CocosForWin-v3.10.exe如果下载不了,可以在这里下http://cdn.cocos2d-x.org ...
- 编程中的链式调用:Scala示例
编程中的链式调用与Linux Shell 中的管道类似.Linux Shell 中的管道 ,会将管道连接的上一个程序的结果, 传递给管道连接的下一个程序作为参数进行处理,依次串联起N个实用程序形成流水 ...