• 后缀自动机


    kuangbin 的模板

    //begin{ kuangbin SAM }
    struct SAM_Node {
      SAM_Node *fa, *next[CHAR];
      int len, id, pos;
      SAM_Node(int _len = 0): fa(0), len(_len), id(0), pos(0) {memset(next, 0, sizeof(next));}
    };
    SAM_Node SAM_node[MAXN * 2], *SAM_root, *SAM_last;
    int SAM_size;
    SAM_Node *newSAM_Node(int len) {SAM_node[SAM_size] = SAM_Node(len); SAM_node[SAM_size].id = SAM_size; return &SAM_node[SAM_size++];}
    SAM_Node *newSAM_Node(SAM_Node *p) {SAM_node[SAM_size] = *p; SAM_node[SAM_size].id = SAM_size; return &SAM_node[SAM_size++];}
    void SAM_init() {SAM_size = 0; SAM_root = SAM_last = newSAM_Node(0); SAM_node[0].pos = 0;}
    void SAM_add(int x, int len) {
      SAM_Node *p = SAM_last, *np = newSAM_Node(p->len + 1);
      np->pos = len; SAM_last = np;
      for(; p && !p->next[x]; p = p->fa) p->next[x] = np;
      if(!p) { np->fa = SAM_root; return;}
      SAM_Node *q = p->next[x];
      if(q->len == p->len + 1) { np->fa = q; return;}
      SAM_Node *nq = newSAM_Node(q);
      nq->len = p->len + 1; q->fa = nq; np->fa = nq;
      for(; p && p->next[x] == q; p = p->fa) p->next[x] = nq;
    }
    void SAM_build(char *s) {
      SAM_init();
      int len = strlen(s);
      for(int i = 0; i < len; i++) SAM_add(s[i] - 'a', i + 1);
    }
    
    int topocnt[MAXN];
    SAM_Node *topsam[MAXN * 2];
    void topo() {
      int n = strlen(s);
      SAM_build(s);
      memset(topocnt, 0, sizeof(topocnt));
      for(int i = 0; i < SAM_size; i++)topocnt[SAM_node[i].len]++;
      for(int i = 1; i <= n; i++)topocnt[i] += topocnt[i - 1];
      for(int i = 0; i < SAM_size; i++)topsam[--topocnt[SAM_node[i].len]] = &SAM_node[i];
    }
    // end{kaungbin SAM}

    求最长公共子串长度

    char s1[MAXN], s2[MAXN];
    void solve() {
      gets(s1); gets(s2);
      SAM_build(s1);
      int ans = 0;
      SAM_Node *p = SAM_root;
      for(int i = 0, t = 0, len = strlen(s2); i < len; ++i) {
        if(p->next[s2[i] - 'a']) {
          p = p->next[s2[i] - 'a'];
          t++;
        } else {
          while(p != NULL && !p->next[s2[i] - 'a'])  p = p->fa;
          if(p == NULL)  p = SAM_root, t = 0;
          else t = p->len + 1, p = p->next[s2[i] - 'a'];
        }
        ans = std::max(ans, t);
      }
      printf("%d
    ", ans);
    }

    求字典序最小循环移位(可用最小表示法)

    char s1[MAXN];
    void solve() {
      gets(s1); SAM_build(s1);
      int len = strlen(s1);
      for(int i = 0; i < len; ++i)  SAM_add(s1[i] - 'a', len + i + 1);
      SAM_Node *p = SAM_root;
      for(int i = 0, t = 0, len = strlen(s1); i < len; ++i) {
        int j = 0;
        while(p->next[j] == NULL && j < 26) j++;
        p = p->next[j];
      }
      int ans = p->pos - len + 1;
      printf("%d
    ", ans);
    }

    依次输出长度为 i (from 1 to |s|) 的所有子串中出现的最多次数

    const int MAXN = 500000+7;

    //
    r(表示当前状态可以在多少个位置上出现) // mi(当前状态能接受的串的最短长度,即 par->val+1) int r[MAXN],mi[MAXN],ret[MAXN]; void solve() { gets(s); topo(); int len = strlen(s); SAM_Node *p = SAM_root; for(int i = 0; i < len; ++i){ p = p->next[s[i]-'a']; r[p->id] = 1; } for(int i = SAM_size - 1; i > 0; --i) { p = topsam[i]; r[p->fa->id] += r[p->id]; mi[p->id] = mi[p->fa->id]; } for(int i = 1;i < SAM_size;++i){ p = topsam[i]; ret[p->len] = std::max(ret[p->len],r[p->id]); //printf("f[%d]=%d ",p->len,r[p->id]); } for(int i = len-1;i>0;--i) ret[i] = std::max(ret[i+1],ret[i]); for(int i = 1;i <= len;++i){ printf("%d ",ret[i]); } }

    上面代码中 MAXN = 250000+7 时返回 WA 

    ~~~~(>_<)~~~~

    字典序第 K 小子串

    int v[MAXN * 2], c[MAXN], son[MAXN][26];
    char ret[MAXN], ch[MAXN][26];
    void solve() {
      gets(s); topo();
      int len = strlen(s);
      SAM_Node *p = SAM_root;
    
      for(int i = 0; i < SAM_size; i++) v[topsam[i]->id] = 1;
      for(int i = SAM_size - 1; i >= 0; --i) {
        p = topsam[i];
        for(int j = 0; j < 26; ++j) {
          if(p->next[j] == NULL) continue;
          int x = p - SAM_root, y = p->next[j] - SAM_root;
          son[x][c[x]] = y, ch[x][c[x]++] = j + 'a', v[p->id] += v[p->next[j]->id];
        }
      }
    
      int n; scanf("%d", &n);
      while(n--) {
        int k, pos = 0, x = 0; scanf("%d", &k);
        while(k > 0) {
          for(int j = 0; j < c[x]; ++j) {
            p = SAM_root + son[x][j];
            if(v[p->id] >= k){k --, ret[pos++] = ch[x][j], x = son[x][j]; break;}
            else k -= v[p->id];
          }
        }
        ret[pos] = 0; puts(ret);
      }
    }

    <2017-08-09 Wed> 添加字典序第 k 小子串

  • 相关阅读:
    BZOJ4892: [Tjoi2017]dna
    BZOJ4307: Maishroom & Class
    NCEE2018游记
    BZOJ3720: Gty的妹子树
    BZOJ5055: 膜法师
    「奇技淫巧」博客园页面美化(差不多是划水
    白痴qwerta的胡言乱语(一句话日度感想?
    关于qwerta
    NOIP2018退役记
    「NOIP2017」「LuoguP3952」 时间复杂度(模拟,栈
  • 原文地址:https://www.cnblogs.com/Forgenvueory/p/7302483.html
Copyright © 2020-2023  润新知