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能不重复不遗漏地枚举每个回文 ...
随机推荐
- delphi odbc远程连接sqlserver
ip设置格式xx.xx.xx.xx,port ip和端口号之间是逗号
- 解决配置Windows Update失败问题
大家都清楚电脑总是需要更新一些补丁,不过,很多系统用户发现更新了补丁之后,开机会出现windows update更新失败的情况,提示“配置Windows Update失败,还原更改,请勿关闭计算机”信 ...
- ajax阻挡设置
在请求时候设置一个变量为false的.请求方法时候用他的值判断是否在请求中,如果是true就是有请求了,就直接return ,不执行后面的方法, 如果是没有请求,就让值为true,然后开始执行ajax ...
- 使用 js,自己写一个简单的滚动条
当我们给元素加上 overflow: auto; 的时候,就会出现滚动条,然而浏览的不同,滚动条的样式大不一样,有些甚至非常丑. 于是就想着自己写一个滚动条,大概需要弄清楚一下这几个点: 1.滚动条 ...
- PostMessage发送WM_CLOSE消息,实现关闭任意程序
#include <Windows.h> #include <tchar.h> int WINAPI _tWinMain(HINSTANCE hInstance, HINSTA ...
- KeepAlived+HaProxy+MyCat+Percona双机热备PXC集群
一.搭建PXC集群 1.环境:centos7+PXC5.7.21+mycat1.6.5 2.卸载mariadb rpm -qa | grep mariadb* yum -y remove mariad ...
- android 开发设计模式---单例模式
要保证单例,需要做以下几步 必须防止外部可以调用构造函数进行实例化,因此构造函数必须私有化. 必须定义一个静态函数获得该单例 单例使用volatile修饰 使用synchronized 进行同步处理, ...
- JavaFX-Stage
1.Stage类继承自Window类,继承了Window类的show()方法,Stage的close()方法实际上是调用了继承自Window类的hide()方法.另外还有Window的setOpaci ...
- getparameter的使用
在做项目的过程中,会遇到跳转的页面,直接打开到里面的子项,这个时候,看了UI给我设计了四个页面,如果做四个页面,肯定是可以实现的.但是这个不符合前端的设计.就在想通过点击传值进去,肯定是能够获取到的. ...
- calss 类
class Role(object): n = 123 # 类变量,像__init__中life_value,money默认参数用类变量,如果多个类的实例那就节省了内存,类变量只在类中存一份,实例中不 ...