zoukankan      html  css  js  c++  java
  • bzoj 3676: [Apio2014]回文串

    Description

    考虑一个只包含小写拉丁字母的字符串s。我们定义s的一个子串t的“出
    现值”为t在s中的出现次数乘以t的长度。请你求出s的所有回文子串中的最
    大出现值。

    Input

    输入只有一行,为一个只包含小写字母(a -z)的非空字符串s。

    Output


    输出一个整数,为逝查回文子串的最大出现值。

    Sample Input

    【样例输入l】
    abacaba

    【样例输入2]
    www

    Sample Output

    【样例输出l】
    7

    【样例输出2]
    4

    HINT

    一个串是回文的,当且仅当它从左到右读和从右到左读完全一样。

    在第一个样例中,回文子串有7个:a,b,c,aba,aca,bacab,abacaba,其中:

    ● a出现4次,其出现值为4:1:1=4

    ● b出现2次,其出现值为2:1:1=2

    ● c出现1次,其出现值为l:1:l=l

    ● aba出现2次,其出现值为2:1:3=6

    ● aca出现1次,其出现值为1=1:3=3

    ●bacab出现1次,其出现值为1:1:5=5

    ● abacaba出现1次,其出现值为1:1:7=7

    故最大回文子串出现值为7。

    【数据规模与评分】

    数据满足1≤字符串长度≤300000。

    Source

    颓了好久了,终于想着来学一点新东西了;

    其实回文自动机(or回文树)还是算一种比较simple的数据结构,其中每一个点代表的是一个回文串;

    具体是用来统计:本质不同的回文串个数,以及,每个回文串的出现次数;

    主要是要弄清几个数组的含义:

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

    2.next[i][c]表示编号为i的节点表示的回文串在两边添加字符c以后变成的回文串的编号

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

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

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

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

    (蒯的.....)

    具体的话可以参见lcf2000的博客...

    一开始有两个点,0表示偶数长度,1表示奇数长度的;

    主要就是不断跳fail指针,直到S[n - len[last] - 1]==S[n](即上一个串-1的位置和新添加的位置是否相同,相同则说明构成回文)

    最后找到了一个cur点,nxt[cur][c]有值说明以前已经有过这个回文串了,只需把次数加1;

    如果不存在,则说明要新建一个节点表示新的回文串,且这个回文串的len显然等于len[cur]+2(len[1]=-1可以省一个特判),然后更新一下fail;

    更新fail就是找到第一个使得S[n - len[last] - 1] == S[n]的last。

    最后统计答案的时候从叶子节点开始加即可

    #include<cstdio>
    #include<algorithm>
    #include<cmath>
    #include<iostream>
    #include<cstring>
    using namespace std;
    typedef long long ll;
    const int N=500050;
    int gi()
    {
      int x=0,flag=1;
      char ch=getchar();
      while(ch<'0'||ch>'9'){if(ch=='-') flag=-1;ch=getchar();}
      while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
      return x*flag;
    }
    int len[N],nxt[N][30],fail[N],tt,cnt[N],last;
    char s[N];
    void insert(int c,int n){
        int cur=last;
        while(s[n-len[cur]-1]!=s[n]) cur=fail[cur];
        if(!nxt[cur][c]){
    	int now=++tt,la=fail[cur];len[now]=len[cur]+2;
    	while(s[n-len[la]-1]!=s[n]) la=fail[la];
    	fail[now]=nxt[la][c],nxt[cur][c]=now;
        }
        last=nxt[cur][c];cnt[last]++;
    }
    int main(){
        len[++tt]=-1;fail[0]=1;
        scanf("%s",s+1);int l=strlen(s+1);
        for(int i=1;i<=l;i++) insert(s[i]-'a',i);
        ll ans=0;for(int i=tt;i>1;i--) cnt[fail[i]]+=cnt[i],ans=max(ans,1ll*len[i]*cnt[i]);
        printf("%lld
    ",ans);
    }
    
  • 相关阅读:
    gerrit权限控制
    kvm虚拟机根目录磁盘扩容
    vim新手指南
    精通 vim 你应该理解的几个名词
    精通 VIM ,此文就够了
    linux下库的使用--动态库
    linux下库的使用--静态库
    linux下程序编译的各个阶段记录
    ASCII码表
    重构的过程记录--之利用系统数据库:
  • 原文地址:https://www.cnblogs.com/qt666/p/6916577.html
Copyright © 2011-2022 走看看