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]);
	}
}

  

posted @ 2018-08-23 19:44  Winniechen  阅读(215)  评论(0编辑  收藏  举报