• BZOJ2565:最长双回文串


    浅谈(Manacher)https://www.cnblogs.com/AKMer/p/10431603.html

    题目传送门:https://www.lydsy.com/JudgeOnline/problem.php?id=2565

    统计一下每个点做靠左的能覆盖它的回文串中心(left_i)和最靠右的能覆盖它的回文串中心(right_i)即可。

    每次用#的(right_i-left_i)直接更新答案就行。

    对于每个回文串中心,在(i-p_i+1)处的(right)与自己取一个(max),在(i+p_i-1)处与自己取(min)。然后倒着枚举更新(left)为后缀最小值,正着枚举更新(right)为前缀最大值即可。

    注意更新答案的时候要判断当前点是否与自己的(left)(right)都不相等,相等就不是双回文了。

    时间复杂度:(O(n))

    空间复杂度:(O(n))

    代码如下:

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    
    const int maxn=2e5+5;
    
    int n,ans;
    char s[maxn];
    int p[maxn],left[maxn],right[maxn];
    
    int read() {
    	int x=0,f=1;char ch=getchar();
    	for(;ch<'0'||ch>'9';ch=getchar())if(ch=='-')f=-1;
    	for(;ch>='0'&&ch<='9';ch=getchar())x=x*10+ch-'0';
    	return x*f;
    }
    
    int main() {
    	scanf("%s",s+1);
    	n=strlen(s+1);
    	for(int i=n;i;i--)
    		s[i<<1]=s[i],s[(i<<1)-1]='#';
    	s[0]='$',s[n<<1|1]='#',n=n<<1|1;
    	memset(left,63,sizeof(left));
    	int id=0,mx=0;
    	for(int i=1;i<=n;i++) {
    		p[i]=i<=mx?min(mx-i+1,p[(id<<1)-i]):1;
    		while(s[i-p[i]]==s[i+p[i]])p[i]++;
    		if(i+p[i]-1>mx)id=i,mx=i+p[i]-1;
    		int pos=i-p[i]+1;right[pos]=max(right[pos],i);
    		pos=i+p[i]-1;left[pos]=min(left[pos],i);
    	}
    	for(int i=1;i<=n;i++)right[i]=max(right[i-1],right[i]);
    	for(int i=n;i;i--)left[i]=min(left[i],left[i+1]);
    	for(int i=1;i<=n;i+=2)
    		if(right[i]!=i&&left[i]!=i)
    			ans=max(ans,right[i]-left[i]);
    	printf("%d
    ",ans);
    	return 0;
    }
    
  • 相关阅读:
    ld Map system.map
    06
    02
    08
    04
    07
    11
    09
    05
    12
  • 原文地址:https://www.cnblogs.com/AKMer/p/10434454.html
Copyright © 2020-2023  润新知