HDU 3948 The Number of Palindromes(Manacher+后缀数组)
题意
求一个字符串中本质不同的回文子串的个数。
$ 1\leq |string| \leq 100000$
思路
好像是回文自动机的裸题,但是可以用 \(\text{Manacher}\) (马拉车)算法配合后缀数组(或配合哈希表)解决。
\(\text{Manacher}\) 算法非常短小精悍,它可以在线性时空内求出以每个点为中心拓展的最远距离,筛出与 \(n\) 同阶个数个回文串,这些回文串包含原串所有本质不同的回文串。
为了判掉奇偶串的问题,我们为字符串穿插一个特殊字符,如字符串 abccba,就变成了 #a#b#c#c#b#a#。
设每个点向两边拓展形成的最远距离(包含本身)为数组 \(p\) ,那么上述字符串对应的 \(p\) 数组如下:
str | # | a | # | b | # | c | # | c | # | b | # | a | # |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
p | 1 | 2 | 1 | 2 | 1 | 2 | 7 | 2 | 1 | 2 | 1 | 2 | 1 |
不难看出,\(\max\{p\}-1\) 就是原串的最长回文串长度,同理每个字符向两边拓展的最远距离也可以得到。
算法流程也比较简短,首先维护两个变量,\(mr(\text{max right})\) 和 \(pos(\text{mid position})\) ,分别表示已经求出右端点最靠右的子串的右端点和中心位置。如果目前要求的 \(i\) 节点没有超过 \(mr\),那么 \(p[i]\) 就可以是 \(p[2*pos-i]\) (对称点),但要对 \(mr-i+1\) 取 \(\min\),因为在 \(mr\) 右边的字符还不知道。如果 \(i\) 节点超过了 \(mr\) ,那就先直接取 \(1\) 。接下来就暴力继续匹配,因为一次成功匹配必然会带动右端点的推动,所以复杂度是 \(O(n)\) 的,代码如下:
void Manacher(char *str,int len)
{
int n=1;
mnc[1]='#';
FOR(i,1,len)mnc[++n]=str[i],mnc[++n]='#';
int mr=0,pos;
FOR(i,1,n)
{
if(i<=mr)p[i]=std::min(p[(pos<<1)-i],mr-i+1);
else p[i]=1;
while(i-p[i]>=1&&i+p[i]<=n&&mnc[i-p[i]]==mnc[i+p[i]])p[i]++;
if(chk_max(mr,i+p[i]-1))pos=i;
}
}
\(\text{Manacher}\) 算法进行 p[i]++
时可以得到所有本质不同的回文串(不保证无重复)。
注意到一个与已经找过的串本质不同的回文串,一定能通过一次推动右端点得到,并且一次推动右端点最多增加一个回文串(如果以这个位置为右端点存在多个回文串,那么不是最长的串肯定在之前算过),这也同时证明了一个串内本质不同的回文子串是 \(O(n)\) 级别的。
那么可以利用 \(\text{Manacher}\) 求出若干个回文串,这一定包含了所有本质不同的回文子串,我们只用对它进行去重即可,后缀数组可以很方便的实现它,对长度相同的串,按 \(rk\) 进行排序,比较 \(\text{lcp}\) 是否为串长即可。
代码
#include<bits/stdc++.h>
#define FOR(i,x,y) for(int i=(x),i##END=(y);i<=i##END;++i)
#define DOR(i,x,y) for(int i=(x),i##END=(y);i>=i##END;--i)
template<typename T,typename _T>inline bool chk_min(T &x,const _T y){return y<x?x=y,1:0;}
template<typename T,typename _T>inline bool chk_max(T &x,const _T y){return x<y?x=y,1:0;}
typedef long long ll;
const int N=2e5+5;
struct SparseTable
{
int st[N][19],bin[(1<<18)+5];
void init(int *arr,int n)
{
bin[1]=0;FOR(i,2,1<<18)bin[i]=bin[i>>1]+1;
FOR(i,1,n)st[i][0]=arr[i];
FOR(j,1,18)FOR(i,1,n-(1<<j)+1)
st[i][j]=std::min(st[i][j-1],st[i+(1<<(j-1))][j-1]);
}
int query(int l,int r)
{
int k=bin[r-l+1];
return std::min(st[l][k],st[r-(1<<k)+1][k]);
}
}SH;
int sa[N],rk[N],H[N],tmp[3][N];
char str[N];
char mnc[N];int p[N];
int n;
struct node
{
int l,r;
bool operator <(const node &_)const
{
if(r-l+1!=_.r-_.l+1)return r-l+1<_.r-_.l+1;
else return rk[l]<rk[_.l];
}
}pld[N];int pc;
void get_SA(char *s,int n,int m)
{
int *x=tmp[0],*y=tmp[1],*c=tmp[2];
memset(tmp,0,sizeof(tmp));
x[n+1]=y[n+1]=0;
FOR(i,1,m)c[i]=0;
FOR(i,1,n)c[x[i]=s[i]]++;
FOR(i,2,m)c[i]+=c[i-1];
DOR(i,n,1)sa[c[x[i]]--]=i;
for(int k=1;k<=n;k<<=1)
{
int p=0;
FOR(i,n-k+1,n)y[++p]=i;
FOR(i,1,n)if(sa[i]>k)y[++p]=sa[i]-k;
FOR(i,1,m)c[i]=0;
FOR(i,1,n)c[x[y[i]]]++;
FOR(i,2,m)c[i]+=c[i-1];
DOR(i,n,1)sa[c[x[y[i]]]--]=y[i];
std::swap(x,y);
p=x[sa[1]]=1;
FOR(i,2,n)x[sa[i]]=y[sa[i]]==y[sa[i-1]]&&y[sa[i]+k]==y[sa[i-1]+k]?p:++p;
if(n==p)break;
m=p;
}
FOR(i,1,n)rk[sa[i]]=i;
int k=0;
FOR(i,1,n)
{
if(k)k--;
if(rk[i]==1)continue;
int j=sa[rk[i]-1];
while(i+k<=n&&j+k<=n&&s[i+k]==s[j+k])k++;
H[rk[i]]=k;
}
SH.init(H,n);
}
int get_lcp(int x,int y)
{
if(x==y)return n-x+1;
x=rk[x],y=rk[y];
if(x>y)std::swap(x,y);
return SH.query(x+1,y);
}
void Manacher(char *str,int len)
{
int n=1;
mnc[1]='#';
FOR(i,1,len)mnc[++n]=str[i],mnc[++n]='#';
pc=0;
int mr=0,pos;
FOR(i,1,n)
{
if(i<=mr)p[i]=std::min(p[(pos<<1)-i],mr-i+1);
else p[i]=1;
while(i-p[i]>=1&&i+p[i]<=n&&mnc[i-p[i]]==mnc[i+p[i]])
{
p[i]++;
if(mnc[i-p[i]+1]=='#')
pld[++pc]=(node){(i-p[i]+1+1)/2,(i+p[i]-1-1)/2};
}
if(chk_max(mr,i+p[i]-1))pos=i;
}
}
int main()
{
int T;
scanf("%d",&T);
FOR(Ti,1,T)
{
scanf("%s",str+1);
n=strlen(str+1);
get_SA(str,n,256);
Manacher(str,n);
std::sort(pld+1,pld+1+pc);
int ans=1;
FOR(i,2,pc)
{
if(pld[i].r-pld[i].l+1==pld[i-1].r-pld[i-1].l+1&&get_lcp(pld[i].l,pld[i-1].l)>=pld[i].r-pld[i].l+1)
continue;
else ans++;
}
printf("Case #%d: %d\n",Ti,ans);
}
return 0;
}
HDU 3948 The Number of Palindromes(Manacher+后缀数组)的更多相关文章
- hdu 3948 The Number of Palindromes
The Number of Palindromes Time Limit: 6000/3000 MS (Java/Others) Memory Limit: 262144/262144 K (J ...
- 【HDU3948】 The Number of Palindromes (后缀数组+RMQ)
The Number of Palindromes Problem Description Now, you are given a string S. We want to know how man ...
- UVA 11475 Extend to Palindrome (kmp || manacher || 后缀数组)
题目链接:点击打开链接 题意:给你一个串,让你在串后面添加尽可能少的字符使得这个串变成回文串. 思路:这题可以kmp,manacher,后缀数组三种方法都可以做,kmp和manacher效率较高,时间 ...
- 【HDOJ】3948 The Number of Palindromes
后缀数组求不重复回文子串数目.注意dp数组. /* 3948 */ #include <iostream> #include <sstream> #include <st ...
- 【HDU 5030】Rabbit's String (二分+后缀数组)
Rabbit's String Problem Description Long long ago, there lived a lot of rabbits in the forest. One d ...
- hdu 4691 最长的共同前缀 后缀数组 +lcp+rmq
http://acm.hdu.edu.cn/showproblem.php? pid=4691 去年夏天,更多的学校的种族称号.当时,没有后缀数组 今天将是,事实上,自己的后缀阵列组合rmq或到,但是 ...
- HDU 5442 Favorite Donut(暴力 or 后缀数组 or 最大表示法)
http://acm.hdu.edu.cn/showproblem.php?pid=5442 题意:给出一串字符串,它是循环的,现在要选定一个起点,使得该字符串字典序最大(顺时针和逆时针均可),如果有 ...
- [APIO2014]回文串 manacher 后缀数组
题面:洛谷 题解: 还是这个性质:对于每个串而言,本质不同的回文串最多只有O(n)个. 所以我们先求出这O(n)个本质不同的回文串,然后对整个串求一次sa. 然后对于每个回文串,求出它的出现次数,更新 ...
- 【题解】回文串 APIO 2014 BZOJ 3676 COGS 1985 Manacher+后缀数组+二分
这题可以用回文自动机来做,但是我并没有学,于是用Manacher+SA的做法O(nlogn)水过 首先,看到回文串就能想到用Manacher 同样还是要利用Manacher能不重复不遗漏地枚举每个回文 ...
随机推荐
- char和QChar(Unicode的编码与内存里的值还不是一回事)
char类型是c/c++中内置的类型,描述了1个字节的内存信息的解析.比如: char gemfield=’g’;那么在由gemfield标记的这块内存的大小就是1个字节,信息就是01100111,8 ...
- java程序如何正确的打日志
什么是日志 简单的说,日志就是记录程序的运行轨迹,方便查找关键信息,也方便快速定位解决问题. 我们 Java 程序员在开发项目时都是依赖 Eclipse/ Idea 等开发工具的 Debug 调试功能 ...
- 【JVM】-NO.113.JVM.1 -【JDK11 HashMap详解-4-resize()】
Style:Mac Series:Java Since:2018-09-10 End:2018-09-10 Total Hours:1 Degree Of Diffculty:5 Degree Of ...
- 五、latex文档的篇章结构
- Your password does not satisfy the current policy requirements问题解决方法
运行 mysql>set validate_password_policy=0; 目的是,可以设置弱密码.
- Json和Ajax
LogDao接口 package com.log.dao; import java.util.List; import com.log.Vo.LogInfoVo; import com.log.bea ...
- PHP提交失败保留填写后的信息
index.html: <html> <head> <title>jQuery Ajax 实例演示</title> </head> < ...
- pygame 游戏舞台搭建典型应用
#两个文件要放在同一目录中,包括图片 一.搭建游戏舞台主程序 #!/usr/bin/env python3#_*_coding:utf-8_*_ author ==$ VACyp import sys ...
- loadrunner12中JavaVuser脚本的编写
1.环境准备: 友情提示:用本地环境,不要用虚拟机 LoadRunner11----->对应JDK1.6版本(32位) LoadRunner12----->对应JDK1 ...
- [批处理]使用IncrediBuilder加速自动化测试过程
背景 1.目前单机全部跑完一次测试的时间大概是5个小时 2.7个虚拟机进行分布式执行的时间大概为3.5小时(包含复制.收集日志的时间) 预期 期望通过IncrediBuilder的虚拟进程的功能在单机 ...