• 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
    ", 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
    ", res);
    134     }
    135 }
    136 
    137 int main() {
    138     init();
    139     init_sam();
    140     solve();
    141     return 0;
    142 }
  • 相关阅读:
    Linux内核的整体框架
    Unix环境高级编程_文件和目录
    Unix环境高级编程_文件I/O
    u-boot启动的第二阶段
    linux基础之vi编辑器设置文件模板
    ARM linux开发之安装配置tftp
    STM32笔试题笔记
    linux基础之find命令常用用法
    ARM linux开发之根文件系统
    ARM linux开发之linux内核启动简介
  • 原文地址:https://www.cnblogs.com/yyf0309/p/7260646.html
Copyright © 2020-2023  润新知