P5446 题解
Aisaka_Taiga · · 题解
一开始我只想出了判断经过一次翻转的处理,然后看到了这篇题解。
感觉大佬讲的不是很清晰,希望我能帮助各位理解。
设给出的前缀串为
从题目来看就可以知道分为两种情况:
-
当前的串经过一次翻转得到的字符串,
s 是此串的前缀; -
当前的串经过多次翻转得到的字符串,
s 是此串的前缀。
对于第一种情况,我们看下面这张图。
蓝红两条竖线分别为以
对于第二种情况,不好用图来解释,我们用样例中的 qwqwq 来解释:我们从样例的输出得知,qw 是可以经过两次翻转得到一个字符串 qw 翻转一次是 qwq,qwq 是可以经过一次翻转就得到 qwqwq 的!也就是说,我们可以利用之前翻转一次就可以的点来寻找需要翻转两次,三次等等才能得到目标串的!那么我们翻转一次后再翻转一次能得到答案的标志是什么?就是我们翻转后的右端点的
code
#include<bits/stdc++.h>
#define int long long
#define N 1000100
using namespace std;
int T,n,p[N<<1],g[N<<1],cnt;
char s[N<<1];
inline void qk()
{
for(int i=1;i<=cnt;i++)
p[i]=g[i]=0;
}
signed main()
{
cin>>T;
while(T--)
{
cnt=0;
char c=getchar();
s[0]='!';s[++cnt]='%';
while(c<'a'||c>'z')c=getchar();
while(c>='a'&&c<='z')s[++cnt]=c,s[++cnt]='%',c=getchar();
s[cnt+1]='#';
qk();
int j=0,mid=0;
for(int i=1;i<=cnt;i++)
{
if(i<=j)p[i]=min(p[2*mid-i],j-i+1);
while(s[i+p[i]]==s[i-p[i]])p[i]++;
if(i+p[i]>j)j=p[i]+i-1,mid=i;
}
// for(int i=2;i<=cnt;i+=2)
// cout<<p[i]-1<<" ";
// cout<<endl;
for(int i=cnt-1;i>=1;i-=2)
{
if(i+p[i]-1>=cnt)g[i]=1;
else if(g[i+p[i]-2]&&i==p[i])g[i]=1;
else continue;
}
for(int i=2;i<=cnt;i+=2)
if(g[i])cout<<i/2<<" ";
cout<<endl;
}
return 0;
}