bzoj3676[APIO2014]回文串

2024-02-04 11:58
文章标签 回文 apio2014 bzoj3676

本文主要是介绍bzoj3676[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。


这道题有两种做法:

1.回文自动机

首先,会了回文自动机的同学就会发现这道题是模板题。代码如下:

#include<bits/stdc++.h>
#define MAXN 300010
#define ll long long
using namespace std;
ll read(){char c;ll x;while(c=getchar(),c<'0'||c>'9');x=c-'0';while(c=getchar(),c>='0'&&c<='9') x=x*10+c-'0';return x;
}
char s[MAXN];
ll fail[MAXN],son[MAXN][26],len[MAXN],cnt[MAXN];
ll tot,last,cur,ans,l;
ll newnode(ll x){len[tot]=x;return tot++;
}
ll get_fail(ll p,ll x){while(s[x-len[p]-1]!=s[x]) p=fail[p];return p;
}
int main()
{scanf("%s",s+1);s[0]=-1;fail[0]=1;last=0;l=strlen(s+1);newnode(0);newnode(-1);register int i;for(i=1;i<=l;i++){cur=get_fail(last,i);if(!son[cur][s[i]-'a']){ll now=newnode(len[cur]+2);fail[now]=son[get_fail(fail[cur],i)][s[i]-'a'];son[cur][s[i]-'a']=now;} cnt[last=son[cur][s[i]-'a']]++;}for(i=tot-1;i>=0;i--){cnt[fail[i]]+=cnt[i];ans=max(ans,len[i]*cnt[i]);}printf("%lld\n",ans);return 0;
} 

另外一种思路是用Manacher+后缀自动机

后缀自动机的一个最大的用处就是查询子串,我们先用马拉车求出每个点的回文子串,然后在后缀自动机上找。但是直接找并不行。我们查询的时候就是在后缀自动机的fail树上从儿子往父亲跳,我们发现这个过程其实是可以用倍增优化的,我们直接倍增往上跳,并且使其一直满足 len[x]>(rl+1) l e n [ x ] > ( r − l + 1 ) ,就可以在 log l o g 的时间内找出现的次数了。
所以最后的复杂度是 O(nlogn) O ( n l o g n )

#include<bits/stdc++.h>
#define MAXN 600005
#define ll long long
using namespace std;
int read(){
    char c;int x;while(c=getchar(),c<'0'||c>'9');x=c-'0';
    while(c=getchar(),c>='0'&&c<='9') x=x*10+c-'0';return x;
}
string s;
int len[MAXN],siz[MAXN],son[MAXN][26],fa[MAXN],t[MAXN],a[MAXN],dep[MAXN],gran[MAXN][19],p[MAXN],fir[MAXN],pos[300005];
int lst=1,node=1,mx,id;
ll ans;
void extend(int c,int id){
    int f=lst,p=++node;lst=p;
    len[p]=len[f]+1;siz[p]++;pos[id]=p;
    while(f&&!son[f][c]) son[f][c]=p,f=fa[f];
    if(!f){fa[p]=1;return;}
    int x=son[f][c],y=++node;
    if(len[x]==len[f]+1){
        fa[p]=x;node--;return;
    }
    fa[y]=fa[x];fa[x]=fa[p]=y;len[y]=len[f]+1;
    memcpy(son[y],son[x],sizeof(son[y]));
    while(f&&son[f][c]==x) son[f][c]=y,f=fa[f];
}
void match(int l,int r){
    if(l<0||r>s.size()) return;
    int p=pos[r];
    for(int i=18;i>=0;i--){
        int temp=gran[p][i];
        if(len[temp]>=r-l+1) p=temp;
    }
    ans=max(1ll*ans,1ll*siz[p]*(r-l+1));
}
void manacher(){
    string t="@#";int x=2;
    for(int i=0;i<s.size();i++) fir[x]=i,t.push_back(s[i]),t.push_back('#'),x+=2;
    for(int i=1;i<t.size();i++){
        p[i]=(mx>i)?min(mx-i,p[id*2-i]):1;
        match(fir[i-p[i]+2],fir[i+p[i]-2]);
        while(t[i+p[i]]==t[i-p[i]]){
          p[i]++;
          match(fir[i-p[i]+2],fir[i+p[i]-2]);
        }
        if(i+p[i]>mx) mx=i+p[i],id=i;
    }
}
int main()
{
    cin>>s;memset(fir,-1,sizeof(fir));
    for(int i=0;i<s.size();i++) extend(s[i]-'a',i);
    for(int i=1;i<=node;i++) t[len[i]]++;
    for(int i=1;i<=node;i++) t[i]+=t[i-1];
    for(int i=1;i<=node;i++) a[t[len[i]]--]=i;
    for(int i=node;i;i--){
        int x=a[i];
        siz[fa[x]]+=siz[x];
    }
    for(int i=1;i<=node;i++){
        int x=a[i];
        dep[x]=dep[fa[x]]+1;gran[x][0]=fa[x];
        for(int j=1;(1<<j)<=dep[x];j++) gran[x][j]=gran[gran[x][j-1]][j-1];
    }
    manacher();
    printf("%lld",ans);
    return 0;
}

这篇关于bzoj3676[APIO2014]回文串的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/677414

相关文章

csu1328(近似回文串)

题意:求近似回文串的最大长度,串长度为1000。 解题思路:以某点为中心,向左右两边扩展,注意奇偶分开讨论,暴力解即可。时间复杂度O(n^2); 代码如下: #include<iostream>#include<algorithm>#include<stdio.h>#include<math.h>#include<cstring>#include<string>#inclu

poj 3974 and hdu 3068 最长回文串的O(n)解法(Manacher算法)

求一段字符串中的最长回文串。 因为数据量比较大,用原来的O(n^2)会爆。 小白上的O(n^2)解法代码:TLE啦~ #include<stdio.h>#include<string.h>const int Maxn = 1000000;char s[Maxn];int main(){char e[] = {"END"};while(scanf("%s", s) != EO

NYOJ 37 回文字符串(记忆化搜索)

OJ题目 : 戳这里~~ 描述 所谓回文字符串,就是一个字符串,从左到右读和从右到左读是完全一样的,比如"aba"。当然,我们给你的问题不会再简单到判断一个字符串是不是回文字符串。现在要求你,给你一个字符串,可在任意位置添加字符,最少再添加几个字符,可以使这个字符串成为回文字符串。 输入 第一行给出整数N(0<N<100) 接下来的N行,每行一个字符串,每个字符串长度不超过1000.

leetcode: 5. 最长回文子串

5. 最长回文子串 题目链接https://leetcode.cn/problems/longest-palindromic-substring/ 题目描述 给定一个字符串 s,找到 s 中最长的回文子串。你可以假设 s 的最大长度为 1000。 示例 1: 输入: "babad" 输出: "bab" 注意: "aba" 也是一个有效答案。 示例 2:

java常用算法之最长回文子串(Longest Palindromic Substring)

方法一:时间复杂度为O(n^3) public static String longestPalindrome1(String s) {int maxPalinLength = 0;String longestPalindrome = null;int length = s.length();// check all possible sub stringsfor (int i = 0; i

力扣-9. 回文数

文章目录 力扣题目代码工程方法1:方法2: 力扣题目 给你一个整数 x ,如果 x 是一个回文整数,返回 true ;否则,返回 false 。 回文数 是指正序(从左向右)和倒序(从右向左)读都是一样的整数。 例如,121 是回文,而 123 不是。 示例 1: 输入:x = 121 输出:true 示例 2: 输入:x = -121 输出:false 解释:从左

psd面试(最长回文子序列+动态规划);

点击打开链接 链接:https://www.nowcoder.com/acm/contest/90/D来源:牛客网 题目描述 掌握未来命运的女神 psd 师兄在拿了朝田诗乃的 buff 后决定去实习。 埃森哲公司注册成立于爱尔兰,是一家全球领先的专业服务公司,为客户提供战略、咨询、数字、技术和运营服务及解决方案。他们立足商业与技术的前沿,业务涵盖40多个行业,以及企业日常运营部门的各个

代码随想录刷题day23丨39. 组合总和,40.组合总和II, 131.分割回文串

代码随想录刷题day23丨39. 组合总和,40.组合总和II, 131.分割回文串 1.题目 1.1组合总和 题目链接:39. 组合总和 - 力扣(LeetCode) 视频讲解:带你学透回溯算法-组合总和(对应「leetcode」力扣题目:39.组合总和)| 回溯法精讲!_哔哩哔哩_bilibili 文档讲解:https://programmercarl.com/0039.%E7%

后缀数组 - 求最长回文子串 + 模板题 --- ural 1297

1297. Palindrome Time Limit: 1.0 second Memory Limit: 16 MB The “U.S. Robots” HQ has just received a rather alarming anonymous letter. It states that the agent from the competing «Robots Unlim

[置顶]论如何优雅的处理回文串 - 回文自动机详解

写在前面 最近无意中看到了这个数据结构,顺便也就学习了一下。 而且发现网上关于这个算法的描述有很多地方是错的,在这里做了一些更正。 处理字符串的算法很多:     KMP,E-KMP,AC自动机,后缀三兄弟:后缀树、后缀数组、后缀自动机,Trie树、Trie图,符串hash... 但以上数据结构在处理回文串上还是稍有欠缺,用这些来处理回文显得太小题大做。 于是有了Manacher算