• 回文树


    应用场景

    1.求串S前缀font>0~i内本质不同回文串的个数(两个串长度不同或者长度相同且至少有一个字符不同便是本质不同)

    2.求串S内每一个本质不同回文串出现的次数

    3.求串S内回文串的个数(其实就是1和2结合起来)

    4.求以下标i结尾的回文串的个数

    参考资料:

    Palindromic Tree——回文树【处理一类回文串问题的强力工具】

    17年国家集训队论文《回文树及其应用》

    定义变量

    1.len 表示编号为i的节点表示的回文串的长度(一个节点表示一个回文串)

    2.next 表示编号为i的节点表示的回文串在两边添加字符c以后变成的回文串的编号(和字典树类似)。

    3.fail 表示节点i失配以后跳转不等于自身的节点i表示的回文串的最长后缀回文串(和AC自动机类似)。

    4.cnt 表示节点i表示的本质不同的串的个数(建树时求出的不是完全的,最后count()函数跑一遍以后才是正确的)

    5.num 表示以节点i表示的最长回文串的最右端点为回文串结尾的回文串个数。

    6.last 指向新添加一个字母后所形成的最长回文串表示的节点。

    7.S 表示第i次添加的字符(一开始设S[0] = -1(可以是任意一个在串S中不会出现的字符))。

    8.p 表示添加的节点个数。

    9.n 表示添加的字符个数。

    由于代码风格不同

    本文全部代码均由上文博客地址中的代码改写而成

    缺点: 速度慢

    例如:  UESTC-1999

    上边的为我的模板的提交结果 TLE
    下边的为改为原博主的模板后的提交结果,1421ms飘过
    时限1500ms

    StatusLengthSubmittedRemoteRunId
    Time Limit Exceeded on test 59 3139 2018-09-03 01:39:57 432786
    StatusTimeMemoryLengthSubmittedRemoteRunId
    Accepted 1421ms 1524kB 2827 2018-09-03 01:51:36 432792

    预定义

     1 const int MAXN = 1e5+10;
     2 const int N = 26;
     3 char s[MAXN];
     4 struct node
     5 {
     6     int next[N];   // next指针,next指针和字典树类似,指向的串为
     7                    // 当前穿两端加上同一个字符构成
     8     int fail;      // fail指针,失配后跳转到fail指针指向的节点
     9     int cnt;       // 表示节点i的本质不同的串的个数
    10                    // 建树时求出的不是完全的,最后Count()函数跑一遍以后才是正确的
    11     int num;       // 表示以节点i表示的最长回文串的最右端点为回文串结尾的回文个数
    12     int len;       // 表示以当前节点表示的最长回文串长度(一个节点表示一个回文串)
    13     int S;         // 存放添加的字符
    14 }pam[MAXN];
    15 int last;   // 指向新添加一个字母后所形成的最长回文串表示的节点
    16 int n;      // 表示添加的字符个数
    17 int p;      // 表示添加的节点个数

    新建节点

    int newnode(int x)
    {
        for(int i = 0;i < N;i++)
            pam[p].next[i] = 0;
        pam[p].cnt = 0;
        pam[p].num = 0;
        pam[p].len = x;
        return p++;
    }

    初始化

     1 void Init()
     2 {
     3     p = 0;
     4     newnode(0);
     5     newnode(-1);
     6     last = 0;
     7     n = 0;
     8     pam[n].S = -1;   // 开头存放一个字符集中没有的字符,减少特判
     9     pam[0].fail = 1;
    10 }

    构造失配指针

    1 int get_fail(int x)
    2 {
    3     while(pam[n-pam[x].len-1].S != pam[n].S)
    4         x = pam[x].fail;
    5     return x;
    6 }

    添加字母

    void Add(int c)
    {
        c -= 'a';
        pam[++n].S = c;
        int cur = get_fail(last);   // 通过上一个回文串找这个回文串的匹配位置
        if(!pam[cur].next[c])       // 如果这个回文串没有出现过,说明出现了
                                    // 一个新的本子不同的回文串
        {
            int now = newnode(pam[cur].len+2);   // 新建节点
            pam[now].fail = pam[get_fail(pam[cur].fail)].next[c];   
            // 和AC自动机一样建立fil指针,以便失败后回跳
            pam[cur].next[c] = now;
            pam[now].num = pam[pam[now].fail].num+1;
        }
        last = pam[cur].next[c];
        pam[last].cnt++;
    }

    Count函数

    1 void Count()
    2 {
    3     for(int i = p-1;i >= 0;--i)
    4     {
    5         pam[pam[i].fail].cnt += pam[i].cnt;
    6         // 父亲累加儿子才cnt,因为如果fail[v] = u , 则u一定是v的子回文串
    7     }
    8 }

    主函数

     1 int main()
     2 {
     3     scanf("%s",s);   // 读入字符串
     4     int len = strlen(s);
     5     Init();          // 初始化
     6     for(int i = 0;i < len;i++)
     7         Add(s[i]);
     8     Count();         // Count()函数跑一遍cnt的值以后才是正确的
     9     printf("balabalabala~~~");
    10     return 0;
    11 }

    原文模板

    速度比我改写的要快
     1 const int MAXN = 100005 ;
     2 const int N = 26 ;
     3 struct Palindromic_Tree {
     4     int next[MAXN][N] ;//next指针,next指针和字典树类似,指向的串为当前串两端加上同一个字符构成
     5     int fail[MAXN] ;//fail指针,失配后跳转到fail指针指向的节点
     6     int cnt[MAXN] ;
     7     int num[MAXN] ;
     8     int len[MAXN] ;//len[i]表示节点i表示的回文串的长度
     9     int S[MAXN] ;//存放添加的字符
    10     int last ;//指向上一个字符所在的节点,方便下一次add
    11     int n ;//字符数组指针
    12     int p ;//节点指针
    13  
    14     int newnode ( int l ) {//新建节点
    15         for ( int i = 0 ; i < N ; ++ i ) next[p][i] = 0 ;
    16         cnt[p] = 0 ;
    17         num[p] = 0 ;
    18         len[p] = l ;
    19         return p ++ ;
    20     }
    21  
    22     void init () {//初始化
    23         p = 0 ;
    24         newnode (  0 ) ;
    25         newnode ( -1 ) ;
    26         last = 0 ;
    27         n = 0 ;
    28         S[n] = -1 ;//开头放一个字符集中没有的字符,减少特判
    29         fail[0] = 1 ;
    30     }
    31  
    32     int get_fail ( int x ) {//和KMP一样,失配后找一个尽量最长的
    33         while ( S[n - len[x] - 1] != S[n] ) x = fail[x] ;
    34         return x ;
    35     }
    36  
    37     void add ( int c ) {
    38         c -= 'a' ;
    39         S[++ n] = c ;
    40         int cur = get_fail ( last ) ;//通过上一个回文串找这个回文串的匹配位置
    41         if ( !next[cur][c] ) {//如果这个回文串没有出现过,说明出现了一个新的本质不同的回文串
    42             int now = newnode ( len[cur] + 2 ) ;//新建节点
    43             fail[now] = next[get_fail ( fail[cur] )][c] ;//和AC自动机一样建立fail指针,以便失配后跳转
    44             next[cur][c] = now ;
    45             num[now] = num[fail[now]] + 1 ;
    46         }
    47         last = next[cur][c] ;
    48         cnt[last] ++ ;
    49     }
    50  
    51     void count () {
    52         for ( int i = p - 1 ; i >= 0 ; -- i ) cnt[fail[i]] += cnt[i] ;
    53         //父亲累加儿子的cnt,因为如果fail[v]=u,则u一定是v的子回文串!
    54     }
    55 } ;
    View Code

    练手习题

    BZOJ3676

    RunIDProblemResultMemoryTimeSubmit_Time
    2937794 3676 Accepted 38036 kb 1252 ms 2018-09-03 00:59:48

    UESTC-1999

    StatusTimeMemoryLengthSubmittedRemoteRunId
    Accepted 1421ms 1524kB 2827 2018-09-03 01:51:36 432792
     
  • 相关阅读:
    C++内存检测函数_CrtSetBreakAlloc()
    Detours 的配置
    浅析杀毒软件开发原理
    Sqlite3初学
    java拦截器、过滤器的区别
    sudo 设置无需密码
    Visual Studio 2012/2010/2008 远程调试
    jquery 随笔
    网页中显示xml,直接显示xml格式的文件
    centos7.2_x64安装mysql.tar.gz
  • 原文地址:https://www.cnblogs.com/duny31030/p/14304922.html
Copyright © 2020-2023  润新知