zoukankan      html  css  js  c++  java
  • Codeforces 917F Substrings in a String

    题目传送门

      传送点I

      传送点II

      传送点III

    题目大意

       给定一个字母串,要求支持以下操作:

    1. 修改一个位置的字母
    2. 查询一段区间中,字符串$s$作为子串出现的次数

    Solution 1 Bitset

      每次匹配一段,可以看成,依次考虑每个位置,匹配的位置对应的起点取交集。例如:

      大概就这个意思。

      bitset的count似乎很慢,可以用__builtin_popcount来数中间的位数,然后暴力数两端的位数会快很多。感觉手写倍增法数位数最快。但有人说前面那个内联函数比手写的$O(log log n)$的速度要快。

    Code

     1 /**
     2  * Codeforces
     3  * Problem#914F
     4  * Accepted
     5  * Time: 2760ms
     6  * Memory: 4300k 
     7  */
     8 #include <bits/stdc++.h>
     9 using namespace std;
    10 typedef bool boolean;
    11 
    12 const int N = 1e5 + 5, alpha = 26;
    13 
    14 int n, m;
    15 char str[N], buf[N];
    16 bitset<N> ch[alpha], ans;
    17 
    18 inline void init() {
    19     scanf("%s", str + 1);
    20     n = strlen(str + 1);
    21     for (int i = 1; i <= n; i++)
    22         ch[str[i] - 'a'][i] = 1;
    23     scanf("%d", &m);
    24 }
    25 
    26 inline void solve() {
    27     int opt, x, y, len;
    28     while (m--) {
    29         scanf("%d%d", &opt, &x);
    30         if (opt == 1) {
    31             scanf("%s", buf);
    32             ch[str[x] - 'a'][x] = 0, ch[buf[0] - 'a'][x] = 1;
    33             str[x] = buf[0];
    34         } else {
    35             scanf("%d%s", &y, buf + 1);
    36             len = strlen(buf + 1);
    37             if (y - x + 1 < len) {
    38                 puts("0");
    39                 continue;
    40             }
    41             ans.set();
    42             for (int i = 1; i <= len; i++)
    43                 ans &= (ch[buf[i] - 'a'] >> (i - 1));
    44 //            for (int i = 1; i <= n; i++)
    45 //                cerr << ans[i] << " ";
    46 //            cerr << endl;
    47 //            for (int i = 1; i <= n; i++)
    48 //                cerr << (ans >> (x - 1))[i];
    49 //            cerr << endl;
    50             int res = (ans >> x).count() - (ans >> (y - len + 2)).count();
    51             printf("%d
    ", res);
    52         }
    53     }
    54 }
    55 
    56 int    main() {
    57     init();
    58     solve();
    59     return 0;
    60 }
    bitset

    Solution 2 Suffix Automaton , Block Division & KMP

      这个是出题人的本意。估计出题人没有想到这道题竟然可以直接被bitset水掉。

      对于在线数一个串的出现次数,排除所有非后缀数据结构。

      由于后缀数据结构都不支持中间带修。因此考虑分块。每一块维护一个SAM。

      要求修改的时候暴力重构一个块的SAM。

      暂且钦定块大小为$C = sqrt{n}$。

    • 如果询问的串长大于$C$,由于询问总串长和$n$同阶,所以这一部分的询问数不会超过$sqrt{n}$个,所以直接暴力KMP,时间复杂度$O(n^{1.5})$
    • 如果询问的串长小于等于$C$,两端涉及到的位置暴力KMP,块间暴力KMP,块内在SAM中查询。这一部分的时间复杂度也是$O(n^{1.5})$

      所以总时间复杂度为$O(n^{1.5})$、

      由于SAM自带常数$26$(字符集大小),所以跑着很慢,sad..另外暴力的过程最好老老实实写KMP,千万不要像我一样直接用SAM来代替,然后无限TLE。。

    Code

      1 /**
      2  * Codeforces
      3  * Problem#917F
      4  * Accepted
      5  * Time: 2995ms
      6  * Memory: 28428k
      7  */ 
      8 #include <bits/stdc++.h>
      9 using namespace std;
     10 typedef bool boolean;
     11 
     12 typedef class TrieNode {
     13     public:
     14         int len, cnt;
     15         TrieNode* ch[26];
     16 //        map<char, TrieNode*> ch;
     17         TrieNode* fail;
     18 }TrieNode;
     19 
     20 const int cs = 350, N = 1e5 + 5;
     21 
     22 typedef class SuffixAutomaton {
     23     public:
     24         int maxlen;
     25         TrieNode* pool;
     26         int *cnt;
     27         TrieNode** sp;
     28         TrieNode *top;
     29         TrieNode *rt, *last;
     30         
     31         SuffixAutomaton(int maxlen = cs + 1):maxlen(maxlen) {
     32             pool = new TrieNode[(maxlen * 2 + 5)];
     33             sp = new TrieNode*[(maxlen * 2 + 5)];
     34             cnt = new int[(maxlen + 1)];
     35         }
     36 
     37         TrieNode* newnode(int len) {
     38 //            top->ch.clear();
     39 //            cerr << top - pool << " " << maxlen << endl;
     40             memset(top->ch, 0, sizeof(top->ch));
     41             top->len = len, top->cnt = 0;
     42             top->fail = NULL;
     43             return top++;
     44         }
     45 
     46         void reset() {
     47             top = pool;
     48             rt = newnode(0);
     49             last = rt;
     50         }
     51 
     52         void extend(char c) {
     53             int x = c - 'a';
     54             TrieNode* p = newnode(last->len + 1);
     55             while (last && !last->ch[x])
     56                 last->ch[x] = p, last = last->fail;
     57             if (!last)
     58                 p->fail = rt;
     59             else {
     60                 TrieNode *q = last->ch[x];
     61                 if (q->len == last->len + 1)
     62                     p->fail = q;
     63                 else {
     64                     TrieNode* nq = newnode(last->len + 1);
     65                     nq->fail = q->fail, p->fail = nq, q->fail = nq;
     66 //                    nq->ch = map<char, TrieNode*>(q->ch);
     67                     memcpy(nq->ch, q->ch, sizeof(nq->ch));
     68                     while (last && last->ch[x] == q)
     69                         last->ch[x] = nq, last = last->fail;
     70                 }    
     71             }
     72             p->cnt++, last = p;
     73         }
     74 
     75         void rebuild(char* str, int l, int r) {
     76             reset();
     77             for (int i = l; i < r; i++)
     78                 extend(str[i]);
     79             memset(cnt, 0, sizeof(int) * (r - l + 2));
     80             for (int i = 0; pool + i < top; i++)    cnt[pool[i].len]++;
     81             for (int i = 1; i <= r - l + 1; i++)    cnt[i] += cnt[i - 1];
     82             for (int i = 0; pool + i < top; i++)
     83                 sp[(cnt[pool[i].len]--) - 1] = pool + i;
     84             for (int i = top - pool - 1; i > 0; i--)    sp[i]->fail->cnt += sp[i]->cnt;
     85         }
     86 
     87         int query(char *str) {
     88             TrieNode* p = rt;
     89                for (int i = 0; str[i] && p; i++)
     90                 p = p->ch[str[i] - 'a'];
     91             return (p) ? (p->cnt) : (0);
     92         }
     93 }SuffixAutomaton;
     94 
     95 int n, m, cc = 0;
     96 int f[N];
     97 char str[N], buf[N];
     98 SuffixAutomaton sam[N / cs + 1];
     99 
    100 inline void init() {
    101     scanf("%s", str);
    102     n = strlen(str);
    103     for (int i = cs; i < n; i += cs, cc++)
    104         sam[cc].reset(), sam[cc].rebuild(str, i - cs, i);
    105     scanf("%d", &m);
    106 }
    107 
    108 #define pick(p) ((l <= p && r >= p) ? (S[p]) : (0))
    109 
    110 int brute(char* S, char* T, int l, int r, int lenT) {
    111     r += 1;
    112     if (r - l < lenT)    return 0;
    113     f[0] = f[1] = 0;
    114     for (int i = 1, j; i < lenT; i++) {
    115         j = f[i];
    116         while (j && T[i] != T[j])    j = f[j];
    117         f[i + 1] = ((T[i] == T[j]) ? (j + 1) : (0));
    118     }
    119 //    for (int i = 0; i <= lenT; i++)
    120 //        cerr << f[i] << " ";
    121 //    cerr << endl;
    122     int rt = 0;
    123     for (int i = l, j = 0; i < r; i++) {
    124         while (j && T[j] != S[i])    j = f[j];
    125         if (T[j] == S[i])    j++;
    126         if (j == lenT)    rt++, j = f[j];
    127     }
    128     return rt;
    129 }
    130 
    131 inline void solve() {
    132     int opt, x, y, len, xi, yi;
    133     while (m--) {
    134         scanf("%d%d", &opt, &x);
    135         x--;
    136         if (opt == 1) {
    137             scanf("%s", buf);
    138             xi = x / cs;
    139             str[x] = buf[0];
    140             if (xi < cc)
    141                 sam[xi].rebuild(str, xi * cs, (xi + 1) * cs);
    142         } else {
    143             scanf("%d%s", &y, buf);
    144             y -= 1, len = strlen(buf);
    145             if (y - x + 1 < len) {
    146                 puts("0");
    147                 continue;
    148             }
    149             xi = x / cs, yi = y / cs;
    150             int res = 0;
    151             if (len >= cs || xi == yi || xi == yi - 1)
    152                 res = brute(str, buf, x, y, len);
    153             else {
    154                 res = brute(str, buf, x, xi * cs + cs + len - 2, len);
    155                 res += brute(str, buf, yi * cs - len + 1, y, len);
    156                 for (int i = xi + 1; i < yi; i++)
    157                     res += sam[i].query(buf);
    158                 for (int i = xi + 2; i < yi; i++)
    159                     res += brute(str, buf, i * cs - len + 1, i * cs + len - 2, len);
    160             }
    161             printf("%d
    ", res);
    162         }
    163     }
    164 }
    165 
    166 int main() {
    167     init();
    168     solve();
    169     return 0;
    170 }
  • 相关阅读:
    C语言位操作
    Ribbon负载规则的替换
    Nginx 的配置文件
    Nginx 操作常用的命令
    Nginx 是什么?
    SpringCloud Eureka 新版本依赖
    @Autowired 与@Resource的区别
    spring 注释
    redis 的 rdb 和 aof 持久化的区别
    jdk1.7下HashMap的头插法问题
  • 原文地址:https://www.cnblogs.com/yyf0309/p/8679199.html
Copyright © 2011-2022 走看看