zoukankan      html  css  js  c++  java
  • 51nod 1600 Simple KMP【后缀自动机+LCT】【思维好题】*

    Description

    对于一个字符串|S|,我们定义fail[i],表示最大的x使得S[1..x]=S[i-x+1..i],满足(x<i)
    显然对于一个字符串,如果我们将每个0<=i<=|S|看成一个结点,除了i=0以外i向fail[i]连边,这是一颗树的形状,根是0
    我们定义这棵树是G(S),设f(S)是G(S)中除了0号点以外所有点的深度之和,其中0号点的深度为-1
    定义key(S)等于S的所有非空子串S'的f(S')之和
    给定一个字符串S,现在你要实现以下几种操作:
    1.在S最后面加一个字符
    2.询问key(S)
    善良的出题人不希望你的答案比long long大,所以你需要将答案对1e9+7取模

    Input

    第一行一个正整数Q
    Q<=10^5
    第二行一个长度为Q的字符串S

    Output

    输出Q行,第i行表示前i个字符组成的字符串的答案

    Input示例

    5
    abaab

    Output示例

    0
    0
    1
    4
    9


    思路

    这题真的好,我用了一个晚上+一个下午才想明白+做出来

    然后说思路
    首先发现一下性质
    考虑分解一个串S的(f(S))的贡献
    为了解决这个问题我们考虑分解每一个前缀的贡献
    那么前缀子串S的贡献怎么统计?

    • 性质1:对于任意一个前缀,它的贡献是除了本身这个子串在S中出现的次数
      • 证明:
        对于一个前缀位置x和一个匹配位置p,
        (G(S))上x一定是p的祖先,因此带来出现次数的贡献

    所以就可以把(f(S))分解成每个前缀的出现次数了
    那么(key(S)=sum_{S'是S的非空子串}f(S'))怎么计算呢?
    这所有的子串可以拆分成若干多个前缀
    所以考虑计算每个子串对(key(s))的贡献,设这个子串的结束位置是(endpos(S')),那么就会对(len(S)-endpos(S')+1)个非空子串产生贡献
    这个贡献并不好统计
    又因为这道题需要计算每一次的增量,反而让我们的计算变得简单了
    因为新增的串只是后缀

    • 性质2:每一次答案增量是上一次的kay(len-1)+新增的后缀在原串中的出现次数
      • 证明:
        上一次的key(len-1)很好理解啊
        因为对于之前的每个子串,产生贡献的子串又多了一个
        也就是变成了相对于原来的(len(S)+1-endpos(S')+1),所以加上(key(len-1))
        然后为什么又新增的后缀在原串中的出现次数呢?
        是因为每次要考虑如果对于一个子串在后面多出现了一次,贡献就需要++
        所以新增的贡献就是除开新增后缀之外的所有后缀出现次数

    然后考虑用LCT动态维护在parent树上的链接关系
    同时维护right和答案
    至于这个答案怎么维护
    因为后缀自动机每个节点表示了一个或多个串,所以在统计的时候需要乘上(t->maxl - t->prt->maxl)的贡献,这样就可以统计所有贡献
    每一次进行扩展的时候可以维护关系,然后扩展结束之后当前节点就表示最后一个点(可以接收整个字符串)
    然后只需要把从这个节点到root的parent树提取出来,先计算答案然后累加贡献就好了


    真的是好题
    墙裂推荐


    //Author: dream_maker
    #include<bits/stdc++.h>
    using namespace std;
    //----------------------------------------------
    //typename
    typedef long long ll;
    //convenient for
    #define for_up(a, b, c) for (int a = b; a <= c; ++a)
    #define for_down(a, b, c) for (int a = b; a >= c; --a)
    #define for_vector(a, b) for (int a = 0; a < (signed)b.size(); ++a)
    //inf of different typename
    const int INF_of_int = 1e9;
    const ll INF_of_ll = 1e18;
    //fast read and write
    template <typename T>
    void Read(T &x){
      bool w = 1;x = 0;
      char c = getchar();
      while(!isdigit(c) && c != '-')c = getchar();
      if(c == '-')w = 0, c = getchar();
      while(isdigit(c)) {
        x = (x<<1) + (x<<3) + c -'0';
        c = getchar();
      }
      if(!w)x=-x;
    }
    template <typename T>
    void Write(T x){
      if(x < 0) {
        putchar('-');
        x=-x; 
      }
      if(x > 9)Write(x / 10);
      putchar(x % 10 + '0');
    }
    //----------------------------------------------
    const int Mod = 1e9 + 7;
    const int N = 1e5 + 10;
    const int CHARSET_SIZE = 26;
    int add(int a, int b) {
      a += b;
      if(a >= Mod) a -= Mod;
      return a;
    }
    int mul(int a, int b) {
      return 1ll * a * b %Mod;
    }
    namespace LCT {
    struct Splay {
      Splay *ch[2], *fa;
      int val, len, sum_len, right;
      int tag; //add tag of siz of right
      //val euqal to sum (len * right)
      Splay():val(0), len(0), sum_len(0), right(0), tag(0){}
    }_null,*null=&_null;
    Splay *newnode() {
      Splay *t=new Splay();
      t->fa = t->ch[0] = t->ch[1] = null;
      return t;
    }
    bool isroot(Splay *t) {
      return t->fa->ch[0] != t && t->fa->ch[1] != t;
    }
    bool Son(Splay *t) {
      return t->fa->ch[1] == t;
    }
    void pushnow(Splay *t, int vl){
      t->tag = add(t->tag, vl);
      t->right = add(t->right, vl);
      t->val = add(t->val, mul(vl, t->sum_len));
    }
    void pushup(Splay *t) {
      t->sum_len = t->len;
      t->val = mul(t->sum_len, t->right);
      if (t->ch[0] != null) {
        t->sum_len = add(t->sum_len, t->ch[0]->sum_len);
        t->val = add(t->val, t->ch[0]->val);
      }
      if (t->ch[1] != null) {
        t->sum_len = add(t->sum_len, t->ch[1]->sum_len);
        t->val = add(t->val, t->ch[1]->val);
      }
    }
    void pushdown(Splay *t) {
      if(!isroot(t))pushdown(t->fa);
      if (!t->tag) return;
      if (t->ch[0] != null) pushnow(t->ch[0], t->tag);
      if (t->ch[1] != null) pushnow(t->ch[1], t->tag);
      t->tag = 0;
    }
    void rotate(Splay *t) {
      Splay *f = t->fa, *g = f->fa;
      bool a = Son(t),b = a ^ 1;
      if (!isroot(f)) g->ch[Son(f)] = t;
      t->fa = g;
      f->ch[a] = t->ch[b];
      t->ch[b]->fa = f;
      t->ch[b] = f;
      f->fa = t;
      pushup(f);
      pushup(t);
    }
    void splay(Splay *t) {
      pushdown(t);
      while (!isroot(t)) {
        Splay *f = t->fa;
        if (!isroot(f)) {
          if (Son(t)^Son(f)) rotate(t);
          else rotate(f);
        }
        rotate(t);
      }
    }
    void access(Splay *t) {
      Splay *tmp = null;
      while (t != null) {
        splay(t);
        t->ch[1] = tmp;
        pushup(t);
        tmp = t;t = t->fa;
      }
    }
    //makeroot function in sam doesn't need rev
    void makeroot(Splay *x) {
      access(x);
      splay(x);
    }
    void cut(Splay *x, Splay *y) {
      makeroot(x);
      splay(y);
      y->ch[1] = null;
      x->fa = null;
      pushup(y);
    }
    void link(Splay *x, Splay *y) {
      makeroot(y);
      x->fa = y;
    }
    };
    using namespace LCT;
    namespace Suffix_Automaton {
    struct Sam {
      Sam *ch[CHARSET_SIZE], *prt;
      int maxl, right;
      Splay *splay; 
      Sam(int maxl = 0, int right = 0):ch(), prt(NULL), maxl(maxl), right(right) {
        splay = newnode();
      }
    }*root, *last;
    void init() {
      last = root = new Sam;
    }
    void modify(Sam *t) {
      t->splay->len = t->maxl - t->prt->maxl;
      t->splay->right = t->right;
      pushup(t->splay);
    }
    int extend(int c) {
      Sam *u = new Sam(last->maxl + 1, 1), *v = last;
      for (;v && !v->ch[c]; v = v->prt) v->ch[c] = u;
      if(!v){
        u->prt = root;
        modify(u);
        link(u->splay, root->splay);
      }else if(v->maxl + 1 == v->ch[c]->maxl){
        u->prt = v->ch[c];
        modify(u);
        link(u->splay, v->ch[c]->splay);
      }else{
        Sam *n = new Sam(v->maxl + 1, 0),*o = v->ch[c];
        copy(o->ch, o->ch + CHARSET_SIZE, n->ch);
        n->prt = o->prt;
        makeroot(o->splay);
        n->right = o->right = o->splay->right;
        modify(n);
        link(n->splay, o->prt->splay);
        cut(o->splay, o->prt->splay);
        o->prt = u->prt = n;
        modify(o);
        modify(u);
        link(o->splay, n->splay);
        link(u->splay, n->splay);
        for(;v && v->ch[c] == o; v = v->prt) v->ch[c] = n;
      }
      last = u;
      makeroot(u->splay);
      Splay *now = u->splay->ch[0];
      int res = now->val;
      pushnow(now, 1);
      return res;
    }
    };
    using namespace Suffix_Automaton;
    int f[N],n;
    char c[N];
    int main() {
      freopen("input.txt", "r", stdin);
      Read(n);
      scanf("%s",c+1);
      Suffix_Automaton::init();
      for_up(i, 1, n)
        f[i] = add(f[i-1], extend(c[i]-'a'));
      for_up(i, 1, n) {
        f[i] = add(f[i], f[i-1]);
        Write(f[i]);
        putchar('
    ');
      }
      return 0;
    }
    
  • 相关阅读:
    Linux开机自动启动ORACLE设置
    Linux Oracle服务启动&停止脚本与开机自启动
    日志挖掘(logminer)
    通过Oracle补充日志,找到锁阻塞源头的SQL
    Oracle Supplemental 补全日志介绍
    修改oracle系统参数spfile导致数据库无法启动解决
    ORA-10997:another startup/shutdown operation of this instance in progress解决方法
    Oracle DataBase单实例使用ASM案例(1)--ASM基本概念
    误删除数据文件,数据库还没有关闭
    2013全国电子设计竞赛进入国赛的项目总结
  • 原文地址:https://www.cnblogs.com/dream-maker-yk/p/9720627.html
Copyright © 2011-2022 走看看