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

    传送门

    Description

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

    Solution

    回文树裸题,或者是“回文自动机”?

    每个节点都是一个回文串,然后fail指针维护的是它的最大的后缀回文串(同时也是前缀的)。

    回文树的用处?

    ——by (PinkRabbit)

    • 统计每个本质不同回文串出现次数的。这个 Manacher 很难做到(需要配合后缀自动机),但是回文自动机可以解决。比如说本题。
    • 有趣的是,回文自动机可以支持前端插入呢,只需要再维护一个指向最长回文前缀的指针 (head)就好啦。因为回文是两边对称的呢,所以前端插入也没关系的。注意两个指针要同时更新哦。就是当前字符插完后,整个串形成回文串,那么就要更新head为当前这一端的fail

    Code 

    #include<bits/stdc++.h>
    #define ll long long
    using namespace std;
    inline int read()
    {
    	int x=0,f=1;char ch=getchar();
    	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    	while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
    	return x*f;
    }
    #define MN 300005
    int fail[MN],len[MN],cnt[MN];
    int c[MN][26],en;ll ans;
    char s[MN];
    int main()
    {
        scanf("%s",s+1);
        fail[0]=1;len[++en]=-1;
        for(int i=1,j=1;s[i];++i)
        {
            while(s[i]!=s[i-len[j]-1]) j=fail[j];
            if(!c[j][s[i]-'a'])
            {
                len[++en]=len[j]+2;
                int k=fail[j];
                while(s[i]!=s[i-len[k]-1])
                    k=fail[k];    
                fail[en]=c[k][s[i]-'a'];
                c[j][s[i]-'a']=en;
            }
            j=c[j][s[i]-'a'];
            ++cnt[j];
        }    
        for(int i=en;i>1;i--)    
        {
            ans=max(ans,1ll*cnt[i]*len[i]);
            cnt[fail[i]]+=cnt[i];    
        }
        printf("%lld
    ",ans);
        return 0;
    }
    


    Blog来自PaperCloud,未经允许,请勿转载,TKS!

  • 相关阅读:
    任务Task系列之Parallel的静态For,ForEach,Invoke方法
    任务Task系列之使用CancellationToken取消Task
    泛型基础
    串的两种模式匹配算法
    数据结构之串类型
    c#基础知识之设计类型
    挣脱
    数据结构之栈和队列
    数据结构之线性表
    NGUI背包系统
  • 原文地址:https://www.cnblogs.com/PaperCloud/p/10252263.html
Copyright © 2011-2022 走看看