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".

Input

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.

Output

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.

Examples

Input

baabaabaaa 5 a ba baa aabaa aaba

Output

7 5 7 3 5

Input

aabbaa 3 aa aabb abba

Output

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

  1 /**
  2  * Codeforces
  3  * Problem#235C
  4  * Accepted
  5  * Time:514ms
  6  * Memory:278100k
  7  */
  8 #include <bits/stdc++.h>
  9 using namespace std;
 10
 11 #define charset 26
 12 //这是一个膜拜大佬的宏定义
 13 #define ModYJQ 2333333
 14
 15 inline int cti(char x) {    return x - ‘a‘;    }
 16
 17 typedef class TrieNode {
 18     public:
 19         TrieNode* next[charset];
 20         TrieNode* fail;
 21         vector<TrieNode*> son;
 22         int cnt;
 23         int len;
 24         int t;
 25         TrieNode():fail(NULL), cnt(0), len(0), t(ModYJQ) {
 26             memset(next, 0, sizeof(next));
 27         }
 28 }TrieNode;
 29
 30 typedef class SuffixAutomachine {
 31     public:
 32         TrieNode *pool;
 33         TrieNode *top;
 34
 35         TrieNode* root;
 36         TrieNode* last;
 37
 38         TrieNode* newnode() {
 39             return top++;
 40         }
 41
 42         TrieNode* newnode(int len) {
 43             top->len = len;
 44             return top++;
 45         }
 46
 47         SuffixAutomachine():pool(NULL), top(NULL), root(NULL), last(NULL) {        }
 48         SuffixAutomachine(int len) {
 49             pool = new TrieNode[len * 2 + 5];
 50             top = pool;
 51             root = newnode();
 52             last = root;
 53         }
 54
 55         inline void extend(char x) {
 56             int c = cti(x);
 57             TrieNode* node = newnode(last->len + 1), *f = last;
 58             node->cnt = 1;
 59             while(f && f->next[c] == NULL)
 60                 f->next[c] = node, f = f->fail;
 61             if(f == NULL)    node->fail = root;
 62             else {
 63                 TrieNode *p = f->next[c];
 64                 if(p->len == f->len + 1)    node->fail = p;
 65                 else {
 66                     TrieNode *clone = newnode(f->len + 1);
 67                     memcpy(clone->next, p->next, sizeof(clone->next));
 68                     clone->fail = p->fail;
 69                     p->fail = clone;
 70                     node->fail = clone;
 71                     while(f && f->next[c] == p)
 72                         f->next[c] = clone, f = f->fail;
 73                 }
 74             }
 75             last = last->next[c];
 76         }
 77 }SuffixAutomachine;
 78
 79 int lens;
 80 int n;
 81 char str[2000005];
 82 SuffixAutomachine sam;
 83
 84 inline void init() {
 85     gets(str);
 86     lens = strlen(str);
 87 }
 88
 89 int dfs(TrieNode* p) {
 90     for(int i = 0; i < (signed)p->son.size(); i++)
 91         p->cnt += dfs(p->son[i]);
 92     return p->cnt;
 93 }
 94
 95 inline void init_sam() {
 96     sam = SuffixAutomachine(lens);
 97     for(int i = 0; i < lens; i++)
 98         sam.extend(str[i]);
 99     for(TrieNode* p = sam.pool; p != sam.top; p++) {
100         if(p->fail)
101             p->fail->son.push_back(p);
102     }
103     dfs(sam.root);
104 }
105
106 inline void solve() {
107     scanf("%d", &n);
108     gets(str);
109     while(n--) {
110         gets(str);
111         lens = strlen(str);
112         memcpy(str + lens, str, sizeof(char) * (lens - 1));
113         TrieNode* p = sam.root, *q;
114         int res = 0;
115         for(int i = 0, nlen = 0, c; i < 2 * lens - 1 && p; i++) {
116             c = cti(str[i]);
117             if(!p->next[c]) {
118                 while(p && !p->next[c]) p = p->fail, nlen = (p) ? (p->len) : (0);
119                 if(!p)    break;
120             }
121             p = p->next[c], nlen++;
122 //            printf("%d %d\n", lens, nlen);
123             if(nlen >= lens) {
124                 q = p;
125                 while(q->fail->len >= lens)
126                     q = q->fail;
127                 if(q->t != n) {
128                     res += q->cnt;
129                     q->t = n;
130                 }
131             }
132         }
133         printf("%d\n", res);
134     }
135 }
136
137 int main() {
138     init();
139     init_sam();
140     solve();
141     return 0;
142 }
时间: 2024-08-04 02:09:14

Codeforces 235C Cyclical Quest - 后缀自动机的相关文章

Codeforces 235C. Cyclical Quest

传送门 写的时候挺蛋疼的. 刚开始的时候思路没跑偏,无非就是建个SAM然后把串开两倍然后在SAM上跑完后统计贡献.但是卡在第二个样例上就是没考虑相同的情况. 然后开始乱搞,发现会出现相同串的只有可能是由一个串无限拼接构成的串,于是上了个KMP来搞循环串..然后就没有然后了 基本上一直处于改了这个错误后又被另一个错误卡着的状态,后来没办法了,翻题解.. 然后发现只需要在SAM的节点上开个标记就行了... 越学越傻啊... 至于为什么KMP会挂掉..求dalao评论指出 //Codeforces 2

【后缀自动机】CodeForces 235C Cyclical Quest

通道: 题意: 思路: 代码: TAG:

后缀自动机(SAM)学习指南

*在学习后缀自动机之前需要熟练掌握WA自动机.RE自动机与TLE自动机* 什么是后缀自动机 后缀自动机 Suffix Automaton (SAM) 是一个用 O(n) 的复杂度构造,能够接受一个字符串所有后缀的自动机. 它最早在陈立杰的 2012 年 noi 冬令营讲稿中提到. 在2013年的一场多校联合训练中,陈立杰出的 hdu 4622 可以用 SAM 轻松水过,由此 SAM 流行了起来. 一般来说,能用后缀自动机解决的问题都可以用后缀数组解决.但是后缀自动机也拥有自己的优点. 1812.

后缀自动机(SAM)

*在学习后缀自动机之前需要熟练掌握WA自动机.RE自动机与TLE自动机* 什么是后缀自动机 后缀自动机 Suffix Automaton (SAM) 是一个用 O(n) 的复杂度构造,能够接受一个字符串所有后缀的自动机. 它最早在陈立杰的 2012 年 noi 冬令营讲稿中提到. 在2013年的一场多校联合训练中,陈立杰出的 hdu 4622 可以用 SAM 轻松水过,由此 SAM 流行了起来. 一般来说,能用后缀自动机解决的问题都可以用后缀数组解决.但是后缀自动机也拥有自己的优点. 1812.

【CF235C】Cyclical Quest(后缀自动机)

[CF235C]Cyclical Quest(后缀自动机) 题面 洛谷 题解 大致翻译: 给定一个串 然后若干组询问 每次也给定一个串 这个串可以旋转(就是把最后一位丢到最前面这样子) 问这个串以及其旋转的串在给定的串中出现了多少次 显然,串可以旋转,那么考虑在后面再接一份就行了 匹配的话就是后缀自动机的匹配 但是额外的注意一点 如果当前匹配出来的最大长度\(>=len\) 也就是当前串的长度 那么就要跳父亲 因为旋转后可能有相同的串出现 所以要开一个数组记录当前节点是否已经算过答案 #incl

Codeforces Round #244 (Div. 2)D (后缀自动机)

Codeforces Round #244 (Div. 2)D (后缀自动机) (标号为0的节点一定是null节点,无论如何都不能拿来用,切记切记,以后不能再错了) 这题用后缀自动机的话,对后缀自动机的很多性质有足够深刻的理解.没想过后缀数组怎么做,因为不高兴敲.... 题意:给出两个长度均不超过5000的字符串s1,s2,求这两个串中,都只出现一次的最短公共子串. 解题思路:求的是公共子串,然后对出现的次数又有限制,第一想法就是后缀自动机啊,后缀自动机处理子串出现次数再合适不过了.做法是这样的

codeforces E. Little Elephant and Strings(广义后缀自动机,Parent树)

传送门在这里. 大意: 给一堆字符串,询问每个字符串有多少子串在所有字符串中出现K次以上. 解题思路: 这种子串问题一定要见后缀自动机Parent树Dfs序统计出现次数都是套路了吧. 这道题统计子串个数,那么可以发现,若一个节点所对应的子串出现了K次,那么其贡献就是len,不需要考虑重复. 因为即使出现重复也是在两个位置. 那么只需统计以每个点结束的子串就好了. 之前的Dfs序就很套路了. 只需再跑一遍字符串,更新答案就好了. 代码: 1 #include<cstdio> 2 #include

Match &amp; Catch CodeForces - 427D 后缀自动机水题

题意: 给出两个字符串a,b,求一个字符串,这个字符串是a和b的子串, 且只在a,b中出现一次,要求输出这个字符串的最小长度. 题解: 将a串放入后缀自动机中,然后记录一下每个节点对应的子串出现的次数 然后把b串取自动机中匹配 然后判断一下 1 #include <set> 2 #include <map> 3 #include <stack> 4 #include <queue> 5 #include <cmath> 6 #include &l

E. Three strings 广义后缀自动机

http://codeforces.com/problemset/problem/452/E 多个主串的模型. 建立一个广义后缀自动机,可以dp出每个状态的endpos集合大小.同时也维护一个R[]表示那个串出现过. 所以可以算出每个状态的dp[i][k]表示第k个串在第i个状态中出现的次数. 可以知道sigma dp[i][0...k]是等于  endpos集合的大小. 然后把这个贡献加到min(i)....max(i)中去就可以了 差分一下. #include <bits/stdc++.h>