zoukankan      html  css  js  c++  java
  • 洛谷P2414 阿狸的打字机

    题意:以trie的形式给出n个字符串,每次询问第x个字符串在第y个字符串中出现了几次。

    解:总串长是n2级别的,所以不能用什么后缀自动机...

    [update]可以建triesam但是不知道trie上的一个节点对应到了sam上的哪几个节点...感觉可以做...好混乱

    听说是个AC自动机上DP,然后怎么想状态都是n²的,然后看题解发现跟DP没啥关系...

    是这样的:对于x在y中出现的每一次,我们把它的前面一直补齐,这样每个串就对应着y的一个前缀,且这个前缀的一个后缀是x。

    可知,trie上y节点到根的链就代表这些前缀。

    然后要求后缀是x。因为x也在这些串中出现了,所以这些前缀一直跳fail的话一定会跳到x节点。即他们全都在x的fail树的子树中。

    然后我们就发现,trie上y到根的路径上的节点和fail树上x的子树的节点的交就是答案。

    一个很朴素的想法就是把这两棵树剖出来,就是一个二维数点问题。把主席树建出来,因为用了树剖所以每次回答询问是nlog²n的,可以做到在线。

    一个高端想法就是离线下来,按照fail树建DFS序。在trie上DFS,维护trie树上当前节点到根的路径权值为1,别的地方为0。

    然后查询就是当在trie上DFS到y的时候,查询x在fail树中的子树权值和。

    这是单点修改,区间查询。树状数组即可。

      1 #include <cstdio>
      2 #include <cstring>
      3 #include <algorithm>
      4 #include <stack>
      5 #include <queue>
      6 #include <vector>
      7 
      8 const int N = 100010;
      9 
     10 struct Edge {
     11     int nex, v;
     12 }edge[N << 1]; int top;
     13 
     14 struct Node {
     15     int x, y, ans;
     16 }node[N];
     17 
     18 int tr[N][26], fail[N], tot = 1;
     19 char str[N];
     20 int ed[N], e[N], pos[N], siz[N], num;
     21 std::vector<int> v[N];
     22 
     23 namespace bit {
     24     int ta[N];
     25     inline void add(int p) {
     26         for(int i = p; i < N; i += i & (-i)) {
     27             ta[i]++;
     28         }
     29         return;
     30     }
     31     inline void del(int p) {
     32         for(int i = p; i < N; i += i & (-i)) {
     33             ta[i]--;
     34         }
     35         return;
     36     }
     37     inline int getSum(int p) {
     38         int ans = 0;
     39         for(int i = p; i >= 1; i -= i & (-i)) {
     40             ans += ta[i];
     41         }
     42         return ans;
     43     }
     44     inline int ask(int l, int r) {
     45         return getSum(r) - getSum(l - 1);
     46     }
     47 }
     48 
     49 inline void add(int x, int y) {
     50     top++;
     51     edge[top].v = y;
     52     edge[top].nex = e[x];
     53     e[x] = top;
     54     return;
     55 }
     56 
     57 inline void getAC() {
     58     std::stack<int> S;
     59     std::queue<int> Q;
     60     int n = strlen(str), t = 0, p = 1;
     61     for(int i = 0; i < n; i++) {
     62         if(str[i] == 'P') {
     63             ed[++t] = p;
     64             continue;
     65         }
     66         if(str[i] == 'B') {
     67             if(!S.empty()) {
     68                 p = S.top();
     69                 S.pop();
     70             }
     71             continue;
     72         }
     73         int f = str[i] - 'a';
     74         if(!tr[p][f]) {
     75             tr[p][f] = ++tot;
     76         }
     77         S.push(p);
     78         p = tr[p][f];
     79     }
     80     // getfail
     81     fail[1] = 1;
     82     Q.push(1);
     83     while(!Q.empty()) {
     84         int x = Q.front();
     85         Q.pop();
     86         for(int f = 0; f < 26; f++) {
     87             if(!tr[x][f]) {
     88                 continue;
     89             }
     90             int y = tr[x][f], j = fail[x];
     91             while(!tr[j][f] && j != 1) {
     92                 j = fail[j];
     93             }
     94             if(tr[j][f] && x != 1) {
     95                 j = tr[j][f];
     96             }
     97             fail[y] = j;
     98             add(j, y);
     99             Q.push(y);
    100         }
    101     }
    102     return;
    103 }
    104 
    105 void DFS_1(int x) {
    106     pos[x] = ++num;
    107     siz[x] = 1;
    108     for(int i = e[x]; i; i = edge[i].nex) {
    109         int y = edge[i].v;
    110         DFS_1(y);
    111         siz[x] += siz[y];
    112     }
    113     return;
    114 }
    115 
    116 void DFS_2(int x) {
    117     bit::add(pos[x]);
    118     for(int i = 0; i < v[x].size(); i++) {
    119         int t = node[v[x][i]].x;
    120         node[v[x][i]].ans = bit::ask(pos[ed[t]], pos[ed[t]] + siz[ed[t]] - 1);
    121     }
    122     for(int i = 0; i < 26; i++) {
    123         if(tr[x][i]) {
    124             DFS_2(tr[x][i]);
    125         }
    126     }
    127     bit::del(pos[x]);
    128     return;
    129 }
    130 
    131 int main() {
    132     scanf("%s", str);
    133     //
    134     getAC();
    135     DFS_1(1);
    136     int m;
    137     scanf("%d", &m);
    138     for(int i = 1; i <= m; i++) {
    139         scanf("%d%d", &node[i].x, &node[i].y);
    140         v[ed[node[i].y]].push_back(i);
    141     }
    142 
    143     DFS_2(1);
    144 
    145     for(int i = 1; i <= m; i++) {
    146         printf("%d 
    ", node[i].ans);
    147     }
    148     return 0;
    149 }
    AC代码
  • 相关阅读:
    C++封装SQLite实例<六>
    大哥给力点 我要发博客
    Android仿人人客户端(v5.7.1)——授权认证(用accessToken换取session_key、session_secret和userId)
    三阶魔方玩法总结
    二阶魔方玩法
    [置顶] C语言实验:等额本金还款法的计算
    活到老、学到老,我要做80岁还能写代码的奇葩!
    百度PHP电话面试之十问
    Java 面向对象编程思想类及其方法的调用,分解质因数,界面交互方法 int string 类型转换 陈光剑
    [置顶] C语言实验:输入任意一个日期的年、月、日的值,求出从公元1年1月1日到这一天总共有多少天,并求出这一天是星期几。
  • 原文地址:https://www.cnblogs.com/huyufeifei/p/10327614.html
Copyright © 2011-2022 走看看