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能不重复不遗漏地枚举每个回文 ...
随机推荐
- TestNG Suite 运行出现中文乱码如何解决
场景: 用TestNG框架运行测试类,控制台视图输出出现中文乱码. 解决方案: 1.eclipse属性>workspace>other>utf-8 2.修改eclipse.ini 文 ...
- ES6 Symbol数据类型和set-map 数据结构
Symbol数据类型 ES6新加的数据类型,提供一个独一无二的值 { let a1 = Symbol() ;let a2 = Symbol() } //声明 { let a3 = Symbol.for ...
- MVC Views文件夹下js无法访问问题解决方案
出现这个问题是因为webconfig做的限制,可修改相应Views下的webconfig文件来解决. <system.webServer> <handlers> <rem ...
- jQuery-AutoComplete自动提示简单实现
注:本次案列实现功能为 用户注册信息,如果数据库对应表中存在部分信息,点击已有的用户的用户名,自动补全其它已有的基本信息 实现思路:通过AutoComplete提示,异步通过用户名查询全表,充当Aut ...
- tp5命令行
手册->命令行->自定义命令行 1.第一步,配置command.php文件 2.第二步,建立命令类文件 注意:该文件中代码,从文档中粘,以防写错. 名字啥的都不用改,就改命名空间 和 定义 ...
- python迭代-如何使用生成器函数实现可迭代对象
如何使用生成器函数实现可迭代对象 问题举例: 实现一个可迭代对象的类,它能迭代出给定范围内 的所有素数: pn = PrimeNumbers(1, 30) for x in pn: print(x) ...
- git删除和提交
//删除git分支git branch -D BranchNamegit branch -r -D origin/BranchNamegit push origin -d BranchName//提交 ...
- 【备忘】mybatis的条件判断用<choose>
mybatis并没有if..else,在mybatis的sql mapper文件中,条件判断要用choose..when..otherwise. <choose> <when t ...
- The world is in my hands
Null项 其实我还是比较希望你能理解我的心情 无聊666回味
- EntityFramework如何创建索引?
一.首先创建一个类 FwEntityTypeConfiguration 继承 EntityTypeConfiguration ,该类完整代码如下: using System.Data.Entity.M ...