[THUPC2018] 绿绿和串串

题目

水题,就当复习马拉车了;

\(vis_i\)表示前缀\(i\)能否变换成功;

如果前缀\(i\)进行一次对称变换,长度为\(i+i-1\);当\(i+i-1>n\)的时候,我们只需要判断回文半径是否不小于\(n-i\)即可;

\(i=i-1\leq n\)时,如果进行变换后得到的是前缀\(i+i-1\),那么\(vis_i=vis_{i+i-1}\),否则\(vis_i=0\);

于是我们只要判断\(i\)进行对称变换后是否为\(i+i-1\),这相当于问\(i\)的回文半径是否为\(i-1\),马拉车即可;

代码

#include
#define re register
#define min(a,b) ((a)<(b)?(a):(b))
const int maxn=1e6+5;int n;
int vis[maxn],r[maxn];char S[maxn];
int main() {
    int T;scanf("%d",&T);
    for(;T;--T) {
        scanf("%s",S+1);n=strlen(S+1);vis[n]=1;
        int R=1,mid=1;for(re int i=1;i<=n;i++)r[i]=0;
        for(re int i=1;i<=n;i++) {
            if(i<=R)r[i]=min(R-i,r[(mid<<1)-i]);
            for(re int j=r[i]+1;j<=i&&j<=n&&S[i+j]==S[i-j];++j)r[i]=j;
            if(i+r[i]>R)R=i+r[i],mid=i;
        }
        for(re int i=n-1;i>1;i--) {
            if(i+i-1>n) {vis[i]=(r[i]>=n-i);continue;}
            vis[i]=(r[i]>=i-1)&vis[i+i-1];
        }
        for(re int i=1;i<=n;i++)if(vis[i])printf("%d%c",i,(i==n)?'\n':' '),vis[i]=0;
    }
}
//g++ lg5446.cpp -o lg5446 -O2 -lm -std=c++11

你可能感兴趣的:([THUPC2018] 绿绿和串串)