P3893 [GDOI2014] Beyond 题解

P3893 [GDOI2014] Beyond 题解

思路

称第一个字符串为 A,第二个字符串 B

考虑枚举环长 L,那么如果 L 是可行的,当且仅当存在一个位置 i,使得 A1i=BLi+1,L,Ai+1L=B1,Li,也就是 A1L 的一个前缀和 B1L 的一个后缀匹配,且此处 A1L 的后缀与 B1L 的前缀匹配。

固定 L 之后可以暴力去寻找 i,但是之后这个方向就很难突破了,所以考虑固定 i,寻找最长的 L,如果设 j=Li,就转化为寻找最大合法的 j,发现这个形式很像 ExKMP,则如果求出了 l=LCP(A1i,B),那么对于所有 j1,lAi+1,i+j=B1j,而对于更大的 j,都是不合法的。

但是当 j=l 的时候,有可能没法满足第一个约束,即 A1i=Bj+1,i+j,所以朴素的做法是枚举所有的 j,然后判断 LCP(Bj+1,i+j,A)i,如果满足这个条件,就可以缩短最长公共前缀来找到一个刚好满足条件的位置。

为了优化做法,用式子表示出来:

maxi=1nmax1jLCP(A1i,B)LCP(Bj+1,i+j,A)i(i+j)

发现底下的式子是二维偏序,所以可以用树状数组解决这个问题。

具体而言,从大到小枚举 i,每次把所以满足 LCP(Bj+1,i+j,A)ij 加入树状数组中,然后每次查询:

i+max1jLCP(A1i,B)j

这样就是前缀 max,也就可以用树状数组维护。

上述 LCP 的值都可以通过 ExKMP 求得。

时间复杂度:O(nlogn)

// Problem: P3893 [GDOI2014] Beyond
// Contest: Luogu
// Author: Moyou
// Copyright (c) 2023 Moyou All rights reserved.
// Date: 2023-12-21 23:25:40

#include <cstring>
#include <iostream>
using namespace std;
const int N = 2e6 + 10, M = 2e6 + 10;

int n;
int z1[N], z2[N], exz1[N], exz2[N];
string a, b;
void Z(string s, int z[]) {
    z[1] = s.size();
    for(int i = 2, l = 0, r = 0; i < s.size(); i ++) {
        if(i <= r) z[i] = min(z[i - l + 1], r - i + 1);
        while(i + z[i] < s.size() && s[i + z[i]] == s[z[i] + 1]) z[i] ++;
        if(i + z[i] - 1 > r) l = i, r = i + z[i] - 1;
    }
}
void exkmp(string a, string b, int z[], int p[]) {
    Z(a, z);
    for(int i = 1, l = 0, r = 0; i < b.size(); i ++) {
        if(i <= r) p[i] = min(z[i - l + 1], r - i + 1);
        while(p[i] + 1 < a.size() && i + p[i] < b.size() && a[p[i] + 1] == b[i + p[i]]) p[i] ++;
        if(i + p[i] - 1 > r) l = i, r = i + p[i] - 1;
    }
}
struct BIT {
    int tr[N];
    void update(int i, int c)  { for (; i <= n; i += i & -i) tr[i] = max(c, tr[i]); }
    int query(int i)  { int res = -1e9; for (; i; i &= i - 1) res = max(res, tr[i]); return res; }
    void clear() {memset(tr, -0x3f, sizeof tr);}
} bit;
int h[N], ne[M], e[M], idx;
void add(int a, int b) {
	e[idx] = b, ne[idx] = h[a], h[a] = idx++; 
}
signed main() {
    ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    cin >> n >> a >> b;
    a = "$" + a, b = "$" + b;
    exkmp(a, b, z1, exz1);
    exkmp(b, a, z2, exz2);
    int ans = 0;
    memset(h, -1, sizeof h);
    for(int i = 1; i <= n; i ++)
        add(exz1[i + 1], i);
    bit.clear();
    for(int i = n; i; i --) {
        for(int j = h[i]; ~j; j = ne[j])
            bit.update(e[j], e[j]);
        ans = max(ans, i + bit.query(exz2[i + 1]));
    }
    cout << ans << '\n';

    return 0;
}

posted @   MoyouSayuki  阅读(7)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
:name :name
点击右上角即可分享
微信分享提示