zoukankan      html  css  js  c++  java
  • BZOJ.2565.[国家集训队]最长双回文串(Manacher/回文树)

    BZOJ
    洛谷

    求给定串的最长双回文串。
    (nleq10^5)

    Manacher:
    (R_i)表示以(i)位置为结尾的最长回文串长度,(L_i)表示以(i)开头的最长回文串长度。答案就是(max{R_i+L_{i+1}})。式子可能会有差别,因为Manacher会在里面加字符。当然我们直接只用'#'位置的(L_i+R_i)就可以更新答案啦。
    Manacher,然后对于位置(i),设它的最远延伸距离是(ex_i)
    然后用(i-j)更新(L_j,;jin[i-ex_i+1, i])(j-i)更新(R_j,;jin[i,i+ex_i-1])(注意字符串里加了'#'的)。

    显然只需要维护更新它的(max{i},min{i})就可以了,可以用线段树。(当然我懒得去写)
    但这个做法显然不够好,考虑能否(O(1))更新,也就是更新一个位置然后递推过来。(表示没想出来= =)

    可以啊...更新(i-j)这个值,相邻两个位置是相差(1)的。而从(j)更新到(i)位置时,(i-j)(leq0)了。也就是我们只更新(L_{i-ex_i+1}=max{ex_i-1}),最后用(L_j=max{L_j, L_{j-1}-1})递推即可。(R)同理。
    这样就(O(n))啦。

    当然我们只需要在'#'处统计,所以只更新这些位置即可。

    回文树:
    对正反串建两次回文树,求出每个位置的最长回文长度就可以了。。


    Manacher:

    //3360kb	84ms
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #define gc() getchar()
    typedef long long LL;
    const int N=2e5+5;
    
    int ex[N],L[N],R[N];
    char s[N];
    
    int Build()
    {
    	int 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]='#', s[(n<<1)+2]='@';
    	return n<<1|1;
    }
    void Manacher(const int n)
    {
    	for(int i=1,id,mx=0; i<=n; ++i)
    	{
    		if(i<mx) ex[i]=std::min(ex[2*id-i],mx-i);
    		else ex[i]=1;
    		while(s[i-ex[i]]==s[i+ex[i]]) ++ex[i];
    		if(i+ex[i]>mx) mx=i+ex[i], id=i;
    		L[i-ex[i]+1]=std::max(L[i-ex[i]+1],ex[i]-1);
    		R[i+ex[i]-1]=std::max(R[i+ex[i]-1],ex[i]-1);
    	}
    }
    
    int main()
    {
    	scanf("%s",s+1);
    	int n=Build(); Manacher(n);
    	for(int i=n-2; i>=1; i-=2) R[i]=std::max(R[i],R[i+2]-2);//i>=1 not i!
    	for(int i=3; i<=n; i+=2) L[i]=std::max(L[i],L[i-2]-2);
    	int ans=0;
    	for(int i=1; i<=n; i+=2) if(L[i]&&R[i]) ans=std::max(ans,L[i]+R[i]);//if!
    	printf("%d
    ",ans);
    
    	return 0;
    }
    

    回文树:

    //23576kb	156ms
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    typedef long long LL;
    const int N=1e5+5;
    
    char s[N];
    struct PAM
    {
    	int las,tot,fail[N],son[N][26],len[N],mx[N];
    
    	PAM() {tot=1, fail[0]=1, len[1]=-1;}
    	inline int Find(int x,int n)
    	{
    		while(s[n-len[x]-1]!=s[n]) x=fail[x];
    		return x;
    	}
    	void Insert(int c,int n)
    	{
    		int p=Find(las,n);
    		if(!son[p][c])
    		{
    			int np=++tot; fail[np]=son[Find(fail[p],n)][c];
    			son[p][c]=np, len[np]=len[p]+2;
    		}
    		las=son[p][c];
    	}
    	void Build(char *s,const int n,const int opt)
    	{
    		if(opt) for(int i=1; i<=n; ++i) Insert(s[i]-'a',i), mx[i]=len[las];
    		else for(int i=1; i<=n; ++i) Insert(s[i]-'a',i), mx[n-i+1]=len[las];//...
    	}
    }p1,p2;
    
    int main()
    {
    	scanf("%s",s+1); int n=strlen(s+1);
    	p1.Build(s,n,1), std::reverse(s+1,s+1+n), p2.Build(s,n,0);
    	int ans=0;
    	for(int i=1; i<n; ++i) ans=std::max(ans,p1.mx[i]+p2.mx[i+1]);
    	printf("%d
    ",ans);
    
    	return 0;
    }
    
  • 相关阅读:
    SpringMVC常用注解
    SpringMVC基础知识
    如何在git中恢复先前的提交?
    git pull 和git fetch的区别
    webpack和gulp的比较
    SpringMVC框架
    Spring框架
    为什么要在一个团队中开展软件测试工作?
    需求测试的注意事项有哪些?
    主键、外键的作用,索引的优点与不足?
  • 原文地址:https://www.cnblogs.com/SovietPower/p/10274688.html
Copyright © 2011-2022 走看看