LOJ#6387 「THUPC2018」绿绿与串串 / String (Manacher)

题目描述

绿绿和 Yazid 是好朋友。他们在一起做串串游戏。

我们定义翻转的操作:把一个串以最后一个字符作对称轴进行翻转复制。形式化地描述就是,如果他翻转的串为 RRR,那么他会将前 ∣R∣−1\left| R\right|-1R1 个字符倒序排列后,插入到串的最后。

举例而言,串abcd进行翻转操作后,将得到abcdcba;串qw连续进行 222次翻转操作后,将得到qwqwq;串z无论进行多少次翻转操作,都不会被改变。

贪玩的绿绿进行了若干次(可能为 000 次)翻转操作。

淘气的绿绿又展示出了一个非空串 SSS,并表示 SSS 是最终的串 RRR 的前缀。现在,他想考考 Yazid,初始的串 RRR 的长度可能是多少。

Yazid 找到了正在参加清华校赛的你,请你来帮他解决这个问题。但聪明的 Yazid 发现,所有超过 ∣S∣\left| S\right|S∣ 的整数都一定是 RRR 的可能长度,因此你只需要告诉他不超过的 ∣S∣\left| S\right|S∣ 的 RRR 的可能长度即可。

为了帮助你理解问题,Yazid 还将对一些概念和记号做出解释:

  • 对于一个串 SSS,∣S∣\left| S\right|S∣ 表示的是该串的长度。

  • 对于一个串 SSS,我们定义串 TTT 是它的前缀,当且仅当 ∣T∣≤∣S∣\left| T\right|\leq\left| S\right|TS∣,且对于任意整数 iii 满足 1≤i≤∣T∣1\leq i\leq\left| T\right|1iT∣,都有 TTT 的左起第 iii 个字符与 SSS 的左起第 iii 个字符相同。(形象地理解,即 TTT 在 SSS 的前部出现)

  • 如:abcabcdefg的前缀,abaabba的前缀,zz的前缀,空串为任意一个串的前缀。

输入格式

输入包含多组数据,第一行一个整数 TTT 表示数据组数。接下来依次描述每组数据,对于每组数据:

  • 一行一个仅由小写字母组成的非空字符串 SSS。

输出格式

对于每组数据,输出 111 行:

  • 从小到大输出 ∣R∣\left| R\right|R∣ 的所有不超过 ∣S∣\left| S\right|S∣ 的可能值,所有值之间用单个空格隔开。

样例

样例输入

4
abcdcb
qwqwq
qaqaqqq
carnation

样例输出

4 6
2 3 4 5
6 7
9

数据范围与提示

保证 ∣S∣≤106\left| S\right|\leq 10^6S106​​,∑∣S∣≤5×106\sum\left| S\right|\leq 5\times 10^6S5×106​​。

∑∣S∣\sum\left| S\right|S∣ 表示的是单个测试点中所有数据 ∣S∣\left| S\right|S∣ 的总和。

天坑已补,在Manacher内一个for循环判断以该点为中心的最长回文子串能不能抵达原串的最右端即可

emmm,具体看代码

#include <cstdio>
#include <iostream>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <queue>
#include <map>
#define ll long long
using namespace std;
const int maxn=1e6+20;

char str[2*maxn],s[maxn];
int len1,len2,vis[maxn*2],p[maxn*2];
vector<int>vc;

void init()
{
    str[0]='$';
    str[1]='#';
    for(int i=0; i<len1; i++)
    {
        str[i*2+2]=s[i];
        str[i*2+3]='#';
    }
    len2=len1*2+2;
    str[len2]='*';
}

void Manacher()
{
    int id=0,mx=0;
    for(int i=1; i<len2; i++)
    {
        if(mx>i) p[i] =min(p[2*id-i],mx-i);
        else p[i]=1;
        for(; str[i-p[i]]==str[i+p[i]]; p[i]++);
        if(p[i]+i>mx)
        {
            mx=p[i]+i;
            id=i;
        }
    }
    //  for(int i=0;i<len2;i++) printf("%c",str[i]);cout<<endl;
    //  for(int i=0;i<len2;i++)printf("%d",p[i]);cout<<endl;
    memset(vis,0,sizeof(vis));
    vc.clear();
    for(int i=len1-1; i>=0; i--)
    {
        int r=p[(i+1)*2]/2;    //以当前位置为中心的最长回文子串长度
        if(i+r==len1) vis[i]=1; //能抵达最右边 合法
        if(i+1-r==0) vis[i]=vis[i+r-1]; //较短,能抵达最左边,若合法,则说明能继续以最右端为中心翻转,此时最右边端点合法
        if(vis[i])  vc.push_back(i+1);
    }
    for(int i=vc.size()-1; i>=0; i--)
        printf("%d%c",vc[i],i==0?'\n':' ');
}
int main()
{
    int t;
    cin>>t;
    while(t--)
    {
        cin>>s;
        len1=strlen(s);
        init();
        Manacher();
    }
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/weimeiyuer/p/9342212.html