Shoi2011 双倍回文

本文介绍了一种使用PAM树进行字符串匹配的方法,并通过倍增跳查进行优化,实现高效查找周期为4的子串。文章详细解释了PAM树的构建过程,插入操作以及如何通过倍增跳查找到符合条件的子串。

题目描述

题解:

建出PAM之后倍增跳查。

貌似很裸。

代码:

#include<queue>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define N 500050
int n;
char s[N];
struct pnt
{
    int pre,len,trs[27];
}p[N];
struct PAM
{
    int las,tot;
    PAM()
    {
        las = tot = 1;
        p[0].pre=p[1].pre=1;
        p[1].len=-1;
    }
    bool mis(int i,int x)
    {
        return s[i]!=s[i-p[x].len-1];
    }
    void insert(int i)
    {
        int lp = las;
        while(mis(i,lp))lp=p[lp].pre;
        int c = s[i]-'a'+1;
        if(!p[lp].trs[c])
        {
            int np = ++tot;
            int tmp = p[lp].pre;
            while(mis(i,tmp))tmp = p[tmp].pre;
            p[np].pre = p[tmp].trs[c];
            p[np].len = p[lp].len+2;
            p[lp].trs[c] = np;
        }
        las = p[lp].trs[c];
    }
    int fa[N][21];
    void build()
    {
        for(int i=0;i<=tot;i++)fa[i][0]=p[i].pre;
        for(int i=1;i<=20;i++)
            for(int j=0;j<=tot;j++)
                fa[j][i]=fa[fa[j][i-1]][i-1];
    }
    int ans;
    void bfs()
    {
        queue<int>q;
        q.push(0);
        while(!q.empty())
        {
            int u = q.front();
            q.pop();
            if(p[u].len%4==0)
            {
                int tmp = u;
                for(int i=20;i>=0;i--)
                    if(p[fa[tmp][i]].len*2>=p[u].len)tmp=fa[tmp][i];
                if(p[tmp].len*2==p[u].len)
                    ans=max(ans,p[u].len);
            }
            for(int i=1;i<=26;i++)
                if(p[u].trs[i])q.push(p[u].trs[i]);
        }
    }
    int cal()
    {
        ans=0;
        bfs();
        return ans;
    }
}pam;
int main()
{
    scanf("%d%s",&n,s+1);
    for(int i=1;i<=n;i++)
        pam.insert(i);
    pam.build();
    printf("%d\n",pam.cal());
    return 0;
}

 

转载于:https://www.cnblogs.com/LiGuanlin1124/p/10127179.html

# P4287 [SHOI2011] 双倍回文 ## 题目描述 记字符串 $w$ 的倒置为 $w^{\mathsf R}$。例如$\tt (abcd)^{\mathsf R}=dcba$,$\tt (abba)^{\mathsf R}=abba$。 对字符串 $x$,如果 $x$ 满足 $x^{\mathsf R}=x$,则称之为回文。例如 $\tt abba$ 是一个回文,而 $\tt abed$ 不是。 如果 $x$ 能够写成 $ww^{\mathsf R} ww^{\mathsf R}$ 形式,则称它是一个“双倍回文”。换句话说,若要 $x$ 是双倍回文,它的长度必须是 $4$ 的倍数,而且 $x$,$x$ 的前半部分,$x$ 的后半部分都要是回文。例如 $\tt abbaabba$ 是一个双倍回文,而 $\tt abaaba$ 不是,因为它的长度不是 $4$ 的倍数。 - $x$ 的子串是指在 $x$ 中连续的一段字符所组成的字符串。例如 $\tt be$ 是 $\tt abed$ 的子串,而 $\tt ac$ 不是。 - $x$ 的回文子串,就是指满足回文性质的 $x$ 的子串。 - $x$ 的双倍回文子串,就是指满足双倍回文性质的 $x$ 的子串。 你的任务是,对于给定的字符串,计算它的最长双倍回文子串的长度。 ## 输入格式 输入分为两行。 第一行为一个整数,表示字符串的长度。 第二行有个连续的小写的英文字符,表示字符串的内容。 ## 输出格式 输出文件只有一行即输入数据中字符串的最长双倍回文子串的长度,如果双倍回文子串不存在,则输出 $0$。 ## 输入输出样例 #1 ### 输入 #1 ``` 16 ggabaabaabaaball ``` ### 输出 #1 ``` 12 ``` ## 说明/提示 ### 数据范围及约定 对于全部数据,$1\le N \le 500000$。 为什么WA了 ```cpp #include <iostream> using namespace std; const int MAX_N = 500050; int n, d[MAX_N * 2], ans; char s[MAX_N], t[MAX_N * 2] = "?"; int main() { cin >> n >> s + 1; for (int i = 1; i <= n; i++) t[2 * i] = s[i], t[2 * i + 1] = '#'; for (int i = 1, l, r = 0; i <= 2 * n + 1; i++) { if (i < r) d[i] = min(d[r - i + l], r - i + 1); for (; t[i - d[i]] == t[i + d[i]]; d[i]++); if (i + d[i] - 1 > r) { for (int j = max(r + 1, i + 4); j <= i + d[i] - 1; j++) if ((j - i) % 4 == 0 && d[i - (j - i) / 2] > (j - i) / 2) ans = max(ans, j - i); l = i - d[i] + 1, r = i + d[i] - 1; } } cout << ans << '\n'; return 0; } ```
08-09
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值