bzoj3676 [Apio2014]回文串

版权声明:虽然是个蒟蒻但是转载还是要说一声的哟 https://blog.csdn.net/jpwang8/article/details/79208117

Description


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

一个串是回文的,当且仅当它从左到右读和从右到左读完全一样。
数据满足1≤字符串长度≤300000。

Solution


第一次接触,吓得我赶紧学习一波
具体就是每个节点记录长度,x向y连边权为w表示x通过首尾添加一个字符w能得到回文串y
与ac自动机类似的,用一个fail记录最长同后缀的回文串。一个比较神奇的操作就是建两个点长度为0、-1来表示奇偶性不同的回文串
需要注意的是统计的时候不能漏一个回文串包含另一个回文串的情况

Code


#include <stdio.h>
#include <string.h>
#include <algorithm>
#define rep(i,st,ed) for (int i=st;i<=ed;++i)
#define drp(i,st,ed) for (int i=st;i>=ed;--i)
typedef long long LL;
const int N=300005;
char st[N];
struct pam {
    int last,cnt,fail[N],rec[N][26],len[N],size[N];
    pam() {len[1]=-1; fail[0]=fail[1]=1; cnt=1;}
    void ins(int x,int n) {
        int p=last;
        while (st[n-len[p]-1]!=st[n]) p=fail[p];
        if (!rec[p][x]) {
            int now=++cnt,k=fail[p]; len[now]=len[p]+2;
            while (st[n-len[k]-1]!=st[n]) k=fail[k];
            fail[now]=rec[k][x]; rec[p][x]=now;
        }
        last=rec[p][x];
        size[last]++;
    }
    void solve() {
        LL ans=0;
        drp(i,cnt,1) {
            size[fail[i]]+=size[i];
            ans=std:: max(ans,(LL)size[i]*len[i]);
        }
        printf("%lld\n", ans);
    }
}pam;
int main(void) {
    // freopen("palindrome.in","r",stdin);
    // freopen("palindrome.out","w",stdout);
    scanf("%s",st+1);
    int len=strlen(st+1);
    rep(i,1,len) {
        pam.ins(st[i]-'a',i);
    }
    pam.solve();
    return 0;
}
阅读更多 登录后自动展开
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页