zoukankan      html  css  js  c++  java
  • 2019 徐州网络赛 G Colorful String 回文树

    题目链接:https://nanti.jisuanke.com/t/41389

    The value of a string sss is equal to the number of different letters which appear in this string.

    Your task is to calculate the total value of all the palindrome substring.

    Input

    The input consists of a single string ∣s∣(1≤∣s∣≤3×105)|s|(1 le |s| le 3 imes 10^5)s(1s3×105).

    The string sss only contains lowercase letters.

    Output

    Output an integer that denotes the answer.

    样例输入

    abac

    样例输出

    6

    样例解释

    abac has palindrome substrings a,b,a,c,aba,ans the total value is equal to 1+1+1+1+2=6

    题意:对于给定的字符串,求每个子回文串中不同的字符个数之和。

    题解:回文树建树求出每不同的回文串出现的次数,再用DFS(开始节点,字母种类),累加求不同字符个数之和

    #include<iostream>
    #include<stdio.h>
    #include <algorithm>
    #include <string>
    #include<string.h>
    #include<math.h>
    #define  ll long long
    using namespace std;
    const int MAXN = 300005 ;
    const int N = 26 ;
    char s[MAXN],ss[MAXN];//输入的要处理的字符串
    ll ans=0;
    int vis[26];
    struct Palindromic_Tree
    {
         int next[MAXN][26] ;//next指针,next指针和字典树类似,指向的串为当前串两端加上同一个字符构成
         int fail[MAXN] ;//fail指针,失配后跳转到fail指针指向的节点
    
         //回文树里面的一个节点就代表一个回文串
         int cnt[MAXN] ;//表示第i个节点代表的回文串出现的次数
         int num[MAXN] ; //表示以节点i表示的最长回文串的最右端点为回文串结尾的回文串个数。
         int len[MAXN] ;//表示第i个节点代表的回文串长度
         
         int S[MAXN] ;//存放添加的字符
         int last ;//指向上一个字符所在的节点,方便下一次add
         int n ;//字符数组指针
         int p ;//节点指针
         int newnode(int l)     //在树中新建节点
         {
               for(int i = 0 ; i < N ; ++ i) next[p][i] = 0 ;
               cnt[p] = 0 ;
               num[p] = 0 ;
               len[p] = l ;
               return p ++ ;
         }
         void init()   //初始化
         {
               p = 0 ;
               newnode(0) ;//建一棵保存长度为偶数的回文树
               newnode(-1) ;//长度为奇数的回文树
               last = 0 ;
               n = 0 ;
               S[n] = -1 ;//开头放一个字符集中没有的字符,减少特判
               fail[0] = 1 ;
         }
         int get_fail(int x)     //和KMP一样,失配后找一个尽量最长的
         {
              while(S[n - len[x] - 1] != S[n]) 
                  x = fail[x] ;
              return x ;
         }
         void add(int c,int pos)
         {
               //printf("%d:",p);//------------>输出节点编号,第一个有回文串的编号时从2开始
               c -= 'a';
               S[++ n] = c ;
               int cur = get_fail(last) ;   //通过上一个回文串找这个回文串的匹配位置
               //printf("%d ",cur);//输出节点编号p代表的回文串的字符长度
               if(!next[cur][c])     //如果这个回文串没有出现过,说明出现了一个新的本质不同的回文串
               {
                     int now = newnode(len[cur] + 2) ;   //新建节点
                     fail[now] = next[get_fail(fail[cur])][c] ;   //和AC自动机一样建立fail指针,以便失配后跳转
                     next[cur][c] = now ;
                     num[now] = num[fail[now]] + 1 ;
               } 
               last = next[cur][c] ;
               cnt[last] ++ ;
               //putchar(10);//------------->输出回车换行
         }
         void count()
         {
               for(int i = p - 1 ; i >= 0 ; -- i) 
                    cnt[fail[i]] += cnt[i] ;
               //父亲累加儿子的cnt,因为如果fail[v]=u,则u一定是v的子回文串!
         }
         void dfs(int x,int y)
         {
                for(int i=0;i<26;i++)
                {
                    if(next[x][i])
                    {
                        int z;//回文串不同字母的个数
                        if(vis[i])//vis标记回文串出现字母的种类
                        {
                            z=y;
                            ans=ans+z*cnt[next[x][i]];
                            dfs(next[x][i],z);
                        }
                        else
                        {
                            vis[i]++;
                            z=y+1;
                            ans=ans+z*cnt[next[x][i]];
                            dfs(next[x][i],z);
                            vis[i]--;//回溯
                        }
                    }
                }
         }
    } pat;
    int main()
    {
        ans=0;
        scanf("%s",s);
        int n=strlen(s);
        pat.init();
        for(int i=0; i<n; i++) 
            pat.add(s[i],i);
        pat.count();
        pat.dfs(0,0);//长度为偶数的回文树
        memset(vis,0,sizeof(vis));
        pat.dfs(1,0);//长度为奇数的回文树
        printf("%lld
    ",ans);
        return 0;
    }
  • 相关阅读:
    函数模板、函数模板特化、重载函数模板、非模板函数重载
    输出流格式化(以操纵子方式格式化,以ios类成员函数方式格式化)
    文件的读写、二进制文件的读写、文件随机读写
    文件流(fstream, ifstream, ofstream)的打开关闭、流状态
    流类库继承体系(IO流,文件流,串流)和 字符串流的基本操作
    对象语义与值语义、资源管理(RAII、资源所有权)、模拟实现auto_ptr<class>、实现Ptr_vector
    operator new 和 operator delete 实现一个简单内存泄漏跟踪器
    异常与继承、异常与指针、异常规格说明
    程序错误、异常(语法、抛出、捕获、传播)、栈展开
    C语言错误处理方法、C++异常处理方法(throw, try, catch)简介
  • 原文地址:https://www.cnblogs.com/-citywall123/p/11544745.html
Copyright © 2011-2022 走看看