【题解】P5446 [THUPC2018]绿绿和串串(manacher)

【题解】P5446 [THUPC2018]绿绿和串串(manacher)

考虑对于一个串进行\(f\)操作,就是让他变成一个以最后一个节点为回文中心的回文串。

那么对于某个位置\(p\),假如它是一个合法的位置,那么它一直倍增一直倍增当长度大于这个原串的时候就使得\(T\)出现过一次了。

  • 倍长一次就大于原串了,此时\(p\)是一个回文中心,且\(p\)的回文半径到达了\(|T|\)
  • 倍长两次才大于原串,此时\(S\)倍增一次后的那个点\(p'\)是一个回文中心,且\(p'\)的回文边界到达了右边界。但为了保证第一次倍增的合法性,此时原\(p\)这个位置的回文半径要顶着\(1\)节点。
  • 倍长三次...

递归地思考,一个位置合法当且仅当这个位置的回文边界顶到原串右边界,或者他的右边界的那个位置合法且自己的左边界顶到原串左边界。

用manacher实现即可,注意一个细节,就是马拉车对于每个非辅助字符的字符,他为中心的回文串的回文边界上的字符一定是个辅助字符,要减去\(1\)以达到效果。

复杂度\(O(2\sum |S|)\)

//@winlere
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>

using namespace std;  typedef long long ll; 
inline int qr(){
      register int ret=0,f=0;
      register char c=getchar();
      while(c<48||c>57)f|=c==45,c=getchar();
      while(c>=48&&c<=57) ret=ret*10+c-48,c=getchar();
      return f?-ret:ret;
}

const int maxn=1e6+5;
char s[maxn<<1];
bool f[maxn<<1];
int p[maxn<<1],cnt,n;
inline void manacher(){
      s[0]='~',s[cnt=1]='|';
      char c=getchar();
      while(c<'a'||c>'z') c=getchar();
      while(c>='a'&&c<='z') s[++cnt]=c,s[++cnt]='|',c=getchar();
      s[cnt+1]='\0';
      for(int t=1,r=0,mid=0;t<=cnt;++t){
        p[t]=0;
        if(r>=t) p[t]=min(r-t+1,p[(mid<<1)-t]);
        while(s[t-p[t]]==s[t+p[t]]) ++p[t];
        if(t+p[t]-1>r) r=t+p[t]-1,mid=t;
      }
}

int main(){
      n=qr();
      for(int t=1;t<=n;++t){
        manacher();
        for(int t=cnt;t;--t) f[t]=0,f[t]=(t+p[t]-1==cnt)||(t-p[t]+1==1&&f[t+p[t]-2]);
        for(int t=2;t<=cnt;t+=2) if(f[t]) printf("%d ",t>>1);
        putchar('\n');
      }
      return 0;
}

posted @ 2019-09-27 16:23 谁是鸽王 阅读(...) 评论(...) 编辑 收藏