manacher
https://winniechen.cn/?p=208 可能这里的更好看一点?
manacher算法
理论上,我觉得这个算法应该是算作DP的一种,这是我的写法,还请不要嘲笑,毕竟这玩意是我自己YY出来的...
manacher算法,用来求最大每个回文中心的最大回文半径...这个东西,我们发现可以通过DP来实现...
我们考虑,维护一个now满足在现在[1,i-1]中now+f[now] \ge f[1...i-1]+1...i-1,现在我们就可以得到一个目前可以的最靠后的回文串...之后针对i 满足f[i]=min(f[(now<<1)-i],f[now]+now-i)也就是求一个已知串中最长的一个回文串,之后在向两侧拓展即可...
时间复杂度的证明:因为最多会拓展n次,所以时间复杂度为O(n)的...
另外,值得注意的是,如果求的回文串长度可以为偶数,就需要在原串的空白位置加上一个特殊字符...比如说#什么的...
例题时间
BZOJ 2565: 最长双回文串
题目很显然,是找到一个#的左侧回文串长度+右侧回文串长度最大...
那么我们可以发现,在维护f[i]数组的同时,维护一个l[i],r[i]的差分表示以i的左右边界的最长回文串...
最后差分一下,l[i]=max(l[i+1]-1,l[i]);之后统计答案...
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <queue>
#include <iostream>
#include <cstdlib>
using namespace std;
#define N 200005
char s[N],str[N];
int n,m,g[N],h[N],f[N],ans=2;
int main()
{
scanf("%s",s+1);n=strlen(s+1);memset(g,0x3f,sizeof(g));
for(int i=1;i<=n;i++)str[++m]='#',str[++m]=s[i];str[++m]='#';f[1]=0;
for(int i=1,now=1;i<=m;i++)
{
f[i]=min(f[(now<<1)-i],f[now]+now-i);
for(;i-f[i]-1>0&&i+f[i]+1<=m;)
{
if(str[i-f[i]-1]!=str[i+f[i]+1])break;
f[i]++;
}//printf("%d\n",f[i]);
if(f[i]+i>f[now]+now)now=i;
g[i+f[i]]=min(g[i+f[i]],i-f[i]);
h[i-f[i]]=max(h[i-f[i]],i+f[i]);
}
for(int i=1;i<=m;i++)h[i]=max(h[i-1]-1,h[i]);
for(int i=m;i;i--)g[i]=min(g[i],g[i+1]+1);
for(int i=1;i<=m;i++)if(str[i]=='#')ans=max(ans,(h[i]-g[i]+1)>>1);
printf("%d\n",ans);return 0;
}
BZOJ 3790: 神奇项链
题目很有问题,但是我最后理解了...求用最少的回文串拼接成原串...
我们把每个$i$为回文中心的回文串拿出来,之后贪心的求一下最小区间覆盖即可。
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <queue>
#include <iostream>
#include <cstdlib>
using namespace std;
#define N 200005
char s[N],str[N];
int n,m,f[N],ans=2;
// void fix(int x,int v){for(;x;x-=x&-x)minn[x]=min(minn[x],v);}
// int find(int x){int ret=1<<30;for(;x<N;x+=x&-x)ret=min(minn[x],ret);return ret;}
struct node{int l,r;}a[N];
bool cmp(const node &a,const node &b){return a.l==b.l?a.r>b.r:a.l<b.l;}
int main()
{
while(scanf("%s",s+1)!=EOF)
{
n=strlen(s+1);m=0;
for(int i=1;i<=n;i++)str[++m]='#',str[++m]=s[i];str[++m]='#';f[1]=0;
for(int i=1,now=1;i<=m;i++)
{
f[i]=min(f[(now<<1)-i],f[now]+now-i);
for(;i-f[i]-1>0&&i+f[i]+1<=m;)
{
if(str[i-f[i]-1]!=str[i+f[i]+1])break;
f[i]++;
}//printf("%d\n",f[i]);
if(f[i]+i>f[now]+now)now=i;
a[i].l=(i-f[i]+1)>>1,a[i].r=(i+f[i])>>1;
// printf("%d %d %d %d\n",a[i].l,a[i].r,i,f[i]);
}sort(a+1,a+m+1,cmp);int mx=0,ans=0;
for(int i=1;mx<n;ans++)
{
int tmp=0;
while(i<=m&&a[i].l<=mx+1)tmp=max(tmp,a[i++].r);mx=tmp;
}printf("%d\n",ans-1);
}return 0;
}
BZOJ 2160: 拉拉队排练
题目大意:求出所有的奇数长度的回文串,之后取前K个即可...
我们发现,我们只需要把每个点作为回文中心的最长回文半径求出来,之后差分一下,求一个前缀和...最后一个快速幂即可...
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <queue>
#include <iostream>
#include <cstdlib>
using namespace std;
#define N 1000005
#define ll long long
#define mod 19930726
char buf[1000005],*p1,*p2;
#define nc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1000005,stdin),p1==p2)?EOF:*p1++)
char s[N];ll K;
int n,m,f[N],ans=2,c[N],d[N];
__attribute__((optimize("-O3")))inline void gc() {
register int x=0;register char c;while(c<'a'||c>'z')c=nc();
while(c<='z'&&c>='a')s[++x]=c,c=nc();
}
__attribute__((optimize("-O3")))ll q_pow(ll x,int n){ll ret=1;for(;n;n>>=1,x=x*x%mod)if(n&1)ret=ret*x%mod;return ret;}
__attribute__((optimize("-O3")))int main()
{
scanf("%d%lld",&n,&K);gc();
f[1]=0;
for(int i=1,now=1;i<=n;i++)
{
f[i]=min(f[(now<<1)-i],f[now]+now-i);
for(;;)
{
if(s[i-f[i]-1]!=s[i+f[i]+1])break;
f[i]++;
}//printf("%d\n",f[i]);
if(f[i]+i>f[now]+now)now=i;
}
for(int i=1;i<=n;i++)c[f[i]+1]--,c[0]++;ll sum=c[0];
for(int i=1;i<=n;i++)c[i]+=c[i-1],sum+=c[i];if(sum<K){puts("-1");return 0;}ll ans=1;
for(int i=n;~i;i--)
{
if(c[i]>=K)
{
printf("%lld\n",ans*q_pow((i<<1)+1,K)%mod);
return 0;
}K-=c[i];ans=ans*q_pow((i<<1)+1,c[i])%mod;
// printf("%d\n",c[i]);
}
}

浙公网安备 33010602011771号