zoukankan      html  css  js  c++  java
  • 回文自动机例题

    今天学习了一下回文自动机,吊打(manacher)有没有(除了空间
    回文自动机基于这两个性质:
    1.一个长度为(n)的字符串的本质不同回文子串是(O(n))级别的
    2.在一个字符串后增加一个字符后,最多新增(1)个本质不同回文子串
    这两条性质都可以用归纳法证明
    于是我们想到用一个结点来代表一个本质不同的回文串,然后就有(PAM)了。和(AC)自动机一样,(PAM)也有(fail)失配边和(ch)转移边,(fail)代表的是最长回文后缀
    采用增量构造法来构建(PAM),在当前字符串末尾插入一个字符(c),如果出现了一个新的回文子串,就新建一个结点,并计算其失配边
    另外最开始有两个结点,分别代表奇数长度的根结点和偶数长度的根结点
    代码如下(细节看注释):

    #define N 100000
    
    int last, nid;
    int ch[N+5][26], fail[N+5], len[N+5], cnt[N+5];
    
    void init() {
      fail[0] = fail[1] = nid = 1; //偶数的根结点为0,fail指向1;奇数为1,fail指向自己
      len[1] = -1; //因为是奇数,所以不是0
    }
    
    void build(char *s) {
      int n = strlen(s+1);
      for(int i = 1; i <= n; ++i) {
        int p = last, c = s[i]-'a';
        while(s[i-len[p]-1] != s[i]) p = fail[p]; //直到能够形成回文串
        if(!ch[p][c]) { //新的本质不同回文串
          int v = ++nid, t = fail[p]; //把t设为fail[p]是因为后缀不能是它本身
          len[v] = len[p]+2;
          while(s[i-len[t]-1] != s[i]) t = fail[t]; //跳fail找最长回文后缀
          fail[v] = ch[t][c]; //跟AC自动机有点像
          ch[p][c] = v;
        }
        last = ch[p][c]; //更新last
        cnt[last]++;
      }
    }
    

    然后[APIO2014]回文串就变成板子了:

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <queue>
    
    using namespace std;
    
    #define N 300000
    
    int last, nid;
    int ch[N+5][26], fail[N+5], len[N+5], cnt[N+5];
    long long ans = 0;
    
    void init() {
      fail[0] = fail[1] = nid = 1;
      len[1] = -1;
    }
    
    void build(char *s) {
      int n = strlen(s+1);
      for(int i = 1; i <= n; ++i) {
        int p = last, c = s[i]-'a';
        while(s[i-len[p]-1] != s[i]) p = fail[p];
        if(!ch[p][c]) {
          int v = ++nid, t = fail[p];
          len[v] = len[p]+2;
          while(s[i-len[t]-1] != s[i]) t = fail[t];
          fail[v] = ch[t][c];
          ch[p][c] = v;
        }
        last = ch[p][c];
        cnt[last]++;
      }
    }
    
    void topo() {
      queue<int> q;
      int in[N+5] = {0};
      for(int i = 0; i <= nid; ++i) in[fail[i]]++;
      for(int i = 0; i <= nid; ++i) if(!in[i]) q.push(i);
      while(!q.empty()) {
        int u = q.front(); q.pop();
        ans = max(ans, 1LL*len[u]*cnt[u]);
        in[fail[u]]--, cnt[fail[u]] += cnt[u];
        if(!in[fail[u]]) q.push(fail[u]);
      }
    }
    
    int main() {
      init();
      char s[N+5] = {0};
      scanf("%s", s+1);
      build(s);
      topo();
      printf("%lld
    ", ans);
      return 0;
    }
    

    还有一道最长双回文串

  • 相关阅读:
    《数字图像处理原理与实践(MATLAB版)》一书之代码Part5
    编程算法
    SVM 输出分类概率(python)
    mysql 数据库通过拷贝文件恢复方法
    ubuntu取消自动登录
    迁移mysql数据位置
    No module named 'lsb_release'
    python3 ssl导入失败
    Linux tar: Cannot change ownership to [..]: Permission denied
    树莓派蓝牙
  • 原文地址:https://www.cnblogs.com/dummyummy/p/10756970.html
Copyright © 2011-2022 走看看