manacher算法与中点算法

P3805 【模板】manacher 算法https://www.luogu.com.cn/problem/P3805,就是求最长回文子串,呃这么说呢,重点:

第一点就是,呃回文中心,可能不在字符串上,那怎么办呢,你可以尝试插入一些原来不在串中的字符,比如说‘#’,就在每两个字符之间插入,就可以保证就算中心不在串上也没有问题就像这样:~|A|B|C|C|B|A|,前面的那个是什么捏,就是边界啦(防止越界)。
第二点( >,< ),就是呃那个维护盒子,(https://blog.csdn.net/qq_43456058/article/details/94588721)与(https://www.zhihu.com/question/37289584)都看起来不错(虽然没认真看),呃其实就是维护r与mid,如果当前节点的回文超出有右边界那就暴力拓展,不然就通过回文性分两种情况讨论即可。(luogu的题解我看的是这个哦)。
注意 p [ i ] 构成的最长度包括了它自己所以后面要减一
代码呀!说起来这个的时间是线性的为什么捏因为O(n)呀(大雾)不想给证明。

#include<bits/stdc++.h>
using namespace std;
int n,m;
int tot=0;
char a[11000002],s[11000002*2];
int p[11000002*2];
int ans=0;
int main()
{
	scanf("%s",a+1);
	s[0]='&';s[++tot]='|';
	for(int i=1;a[i];i++) s[++tot]=a[i],s[++tot]='|';//不要每次都算strlen直接用这个就好
	for(int r=0,mid=0,i=1;i<=tot;i++)
	{
		if(i<=r) p[i]=min(p[mid*2-i],r-i+1);//这一句可以手推一下中点公式即可
		while(s[i-p[i]]==s[i+p[i]]) p[i]++;//如果能拓展的话,注意p [ i ]会先拓展自己所以不会出现 p[i] = 0的情况
		if(p[i]+i>r) r=p[i]+i-1,mid=i;//如果超出边界
		if(p[i]>ans) ans=p[i];
	}
	printf("%d",ans-1);
 	return 0;
}

接下来第一题是这个:P4555 [国家集训队]最长双回文串,呃确实看起来奇奇怪怪的题目,但是只要用递推式就变得离谱了呢,就保存 ll 以i为左端点的最长的回文串,rr 以i为右端点的最长的回文串,so就这样,对模板做了一点优化,不再是最后减一,而是在过程中减去了。题解:https://www.luogu.com.cn/problem/solution/P4555
代码:

#include<bits/stdc++.h>
using namespace std;
int n,m;
int net[100001];
int len,len2;
char a[100001],s[220001];
int f[1000001],p[100001];
int ll[110001],rr[100001];//ll指的是 以i开头的最长串,rr指的是以i结尾的最长串
//因为字符串不能重叠,所以我们只能枚举“ | ” 
int main()
{
	scanf("%s",a+1);
	int tot=strlen(a+1);
	s[0]='~',s[++len]='|';
	for(int i=1;i<=tot;i++) s[++len]=a[i],s[++len]='|';
	for(int i=1,mid=0,r=0;i<=len;i++)
	{
		if(i<=r) p[i]=min(p[mid*2-i],r-i+1);
		while(s[i-p[i]]==s[i+p[i]]) p[i]++;
		if(r<p[i]+i) mid=i,r=i+p[i]-1;
		ll[i-p[i]+1]=max(ll[i-p[i]+1],p[i]-1);//这就是推啦
		rr[i+p[i]-1]=max(rr[i+p[i]-1],p[i]-1);//嗯嗯
	}
	for(int i=2;i<=len;i+=2) ll[i]=max(ll[i],ll[i-2]-2);//就是回文串中间其实是没有算的所以要算一下
	for(int i=len;i>=2;i-=2) rr[i]=max(rr[i],rr[i+2]-2);//嗯嗯
	int ans=0;
	for(int i=2;i<=len;i++)
	{
		if(ll[i]&&rr[i]) ans=max(ans,ll[i]+rr[i]);//如果可行不为0
	}
	printf("%d",ans);
	return 0;
}

下一题冲!
P1659 [国家集训队]拉拉队排练,确实离谱啊,这一题让我想起来多年未见的快速幂模板,实则不难,但处理方式还是要学一学,总结一下就是统计之前用过的,新的就用快速幂处理,然后分两种情况:

if(sum>k) 

以及

if(sum<=k)

这两种就好了,代码如下:

#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,m,k;
char s[2000001];
int cnt[2000001],p[2000001];
int ksm(int a,int k)
{
	if(a==1) return 1;
	int rt=1;
	while(k>0)
	{
		if(k%2!=0) rt=(rt*a)%19930726;
		a*=a;a%=19930726;
		k>>=1;
	}
	return rt;
}

signed main()
{
	scanf("%lld%lld",&n,&k);
	scanf("%s",s+1);
	s[0]='~';//因为这题只要奇数个所以不用做隔板
	for(int i=1,r=0,mid=0;i<=n;i++)
	{
		if(i<=r) p[i]=min(p[mid*2-i],r-i+1);
		while(s[i+p[i]]==s[i-p[i]]) p[i]++; 
		if(p[i]+i>r) mid=i,r=p[i]+i-1;
		cnt[p[i]*2-1]++; 
	}
	int sum=0,ans=1;
	if(n%2==0) n--;
	for(int i=n;i>=1;i-=2)
	{
		sum+=cnt[i];
		if(sum>k) 
		{
			ans*=ksm(i,k);
			ans%=19930726;
			break;
		}
		else 
		{
			ans*=ksm(i,sum);
			ans%=19930726;
			k-=sum;
		}
	}
	if(sum<k) printf("-1");
	else printf("%lld",ans);
}

冲!P5446 [THUPC2018]绿绿和串串,一道结论紫题,还好吧,打这题的时候太晚了就没写什么直接看题解吧:https://www.luogu.com.cn/problem/solution/P5446
代码:

#include<bits/stdc++.h>
using namespace std;
int n,m;
int v[1000005*2],p[1000005*2];
char s[1000005*2],a[1000005*2],s1[1000005*2];
int main()
{
	int t;
	scanf("%d",&t);
	while(t--)
	{
		int len=0;
		memset(v,0,sizeof(v));
		memset(p,0,sizeof(p));
		scanf("%s",a+1);
		n=strlen(a+1);
		for(int i=1;i<=1000005*2;i++) s[i]=s1[i];//之前忘记清空所以好像没拿到分问题不大
		s[0]='~',s[++len]='|';
		for(int i=1;i<=n;i++) s[++len]=a[i],s[++len]='|';
		for(int i=1,r=0,mid=0;i<=len;i++)
		{
			if(i<=r) p[i]=min(p[mid*2-i],r-i+1);
			while(s[i-p[i]]==s[i+p[i]]) p[i]++;
			if(i+p[i]>r) r=i+p[i]-1,mid=i;
		}
		for(int i=len;i>=1;i--)
		{
			if(i+p[i]-1==len) v[i]=1;
			else if(v[i+p[i]-2]&&i==p[i]) v[i]=1;//如果它能翻转成某个最长翻转子串,如 qwqwq 的 qwqw 的 qw ,且不会越界
		}
		for(int i=1;i<=len;i++) 
		{
			if(s[i]>='a'&&s[i]<='z'&&v[i]) printf("%d ",i/2);
		}
		printf("\n");
	}
	return 0;
 } 

最后一题!
P6216 回文匹配
难的要死,明天早上写(现在补),可恶,消息好坏参半,呃呃呃就说这道题不简单,比上一道难很多啊,看起来是kmp+马拉车,做起来才发现,要来一个二次前缀和,(https://www.luogu.com.cn/problem/solution/P6216)挂一下luogu的题解 ,开始想的是n*n的算法直接用kmp算出匹配串然后再用manacher枚举区间然后就可以安稳的暴毙,想优化,那怎么办呢,于是我们推一下公式,直接挂别人的吧:所以在这里插入图片描述
所以就这样子啦,不得不说这个二次前缀和真的很灵性。
注意代码来啦!

#include<bits/stdc++.h> 
#define int long long 
using namespace std;
int n,m;
char s[3001001],t[3001001];
int len1,len2;
int net[3001001],p[3001001];
int sum[3001001];
signed main()
{
	scanf("%lld%lld",&n,&m);
	scanf("%s%s",s+1,t+1);
	net[0]=net[1]=0;
	len1=strlen(s+1);len2=strlen(t+1);
	s[0]='~';t[len2+1]='~';
	for(int i=2,j=0;i<=len2;i++)
	{
		while(j&&t[i]!=t[j+1]) j=net[j];
		if(t[i]==t[j+1]) j++;
		net[i]=j;
	}
	for(int i=1,j=0;i<=len1;i++)
	{
		while(j&&s[i]!=t[j+1]) j=net[j];
		if(s[i]==t[j+1]) j++;
		if(j==len2) 
		{
			sum[i-len2+1]++;
			j=net[j];
		}
	}
	for(int i=1;i<=len1;i++) sum[i]+=sum[i-1];
	for(int i=1;i<=len1;i++) sum[i]+=sum[i-1];
	for(int i=1,r=0,mid=0;i<=len1;i++)
	{
		if(i<=r) p[i]=min(p[mid*2-i],r-i+1);
		while(s[i+p[i]]==s[i-p[i]]) p[i]++;
		if(p[i]+i>r) mid=i,r=i+p[i]-1;	
	}
	int ans=0;
	for(int i=1;i<=len1;i++)
	{
		if(2*p[i]-1<len2) continue ;
		int l=i-p[i]+1,r=i+p[i]-1;
		l--;r=r-len2+1;
		int mid=(l+r)/2;
		ans+=(sum[r]-sum[mid]-sum[((l+r)&1)?mid:mid-1]+sum[l-1]);//注意这句是抄别人的主要是判断奇偶性
		ans%=4294967296;
	}
	printf("%lld",ans);
	return 0;
	
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值