P5028 Annihilate

\(\color{#0066ff}{ 题目描述 }\)

黑暗之主的蜈蚣几乎可以毁灭一切,因此小正方形陷入了苦战……

小正方形现在需要减弱黑暗之主的攻击。

一个黑暗之主的攻击可以用一个仅有小写字母的字符串表示。

现在黑暗之主向小正方形发动了若干攻击,对于两个攻击,小正方形能选出它们最长的公共子串,并把这一段消除。

现在小正方形想要知道,对于任意两个黑暗之主的攻击,它们的最长公共子串长度是多少,你能帮帮它吗?

\(\color{#0066ff}{输入格式}\)

第一行为一个整数 \(n\),表示字符串数目。

接下来 \(n\) 行,一行一个字符串,保证所有字符串长度之和 <= 1000000

\(\color{#0066ff}{输出格式}\)

输出共有\(n\) 行,每行 \(n - 1\)个正整数

第一行第一个正整数表示第1个串与第2个串的最长公共子串

第二个正整数表示第1个串与第3个串的最长公共子串

……

第二行第一个正整数表示第2个串与第1个串的最长公共子串

以此类推。

\(\color{#0066ff}{输入样例}\)

3
abb
bcc
aba

\(\color{#0066ff}{输出样例}\)

1 2
1 1
2 1

\(\color{#0066ff}{数据范围与提示}\)

对于 \(30\%\) 的数据,\(n \leq 5\),每个字符串长度 \(\leq 500\)
对于 \(100\%\) 的数据,\(2\leq n\leq 50\),字符串长度之和 \(\leq 1000000\)
注意:本题内存限制仅为 64 MB,请尽量使用内存运用优秀的方法。

另外,对于占 60 Pts 的测试点,您每通过一个点即可获得 10 Pts

对于剩下的测试点,您只有全部通过才能获得 40 Pts.

对于所有数据点,不保证数据为随机生成。

\(\color{#0066ff}{ 题解 }\)

呜呜呜~~~~这题卡SAM!!!(64MB)

于是写SA

把所有字符串拼起来,每两个之间用完全不同的xjb字符连接

然后跑SA

要找最长公共字串,显然\(O(n^2len)\)是过不了的qwq

考虑\(O(nlen)\)的方法

我们要找的是最长的,又因为ans是一段h的min,所以肯定找最近的(贪心)

记pre[i]为当前第i个字符串最近一次出现的位置

然后每次用ST表查询区间min

TM倪大野, 还尼玛卡我空间

ST表TM会MLE7个点

于是,pre[i]不再维护位置了,维护最近一次出现的位置到当前的\(h_{min}\)

这样每次更新一下,就行了

恶心

#include<bits/stdc++.h>
using namespace std;
#define LL long long
LL in() {
    char ch; int x = 0, f = 1;
    while(!isdigit(ch = getchar()))(ch == '-') && (f = -f);
    for(x = ch ^ 48; isdigit(ch = getchar()); x = (x << 1) + (x << 3) + (ch ^ 48));
    return x * f;
}
const int maxn = 1e6 + 52;
const int inf = 0x7f7f7f7f;
int rk[maxn], x[maxn], y[maxn], c[maxn], sa[maxn], len[55], s[maxn], ht[maxn];
int n, m, t;
void SA() {
    m = 600, t = len[n] + 1;
    for(int i = 1; i <= t; i++) c[x[i] = s[i]]++;
    for(int i = 1; i <= m; i++) c[i] += c[i - 1];
    for(int i = t; i >= 1; i--) sa[c[x[i]]--] = i;
    for(int k = 1; k <= t; k <<= 1) {
        int num = 0;
        for(int i = t - k + 1; i <= t; i++) y[++num] = i;
        for(int i = 1; i <= t; i++) if(sa[i] > k) y[++num] = sa[i] - k;
        for(int i = 1; i <= m; i++) c[i] = 0;
        for(int i = 1; i <= t; i++) c[x[i]]++;
        for(int i = 1; i <= m; i++) c[i] += c[i - 1];
        for(int i = t; i >= 1; i--) sa[c[x[y[i]]]--] = y[i], y[i] = 0;
        std::swap(x, y);
        x[sa[1]] = 1, num = 1;
        for(int i = 2; i <= t; i++) 
            x[sa[i]] = (y[sa[i]] == y[sa[i - 1]] && y[sa[i] + k] == y[sa[i - 1] + k])? num : ++num;
        if(num == t) break;
        m = num;
    }
    for(int i = 1; i <= t; i++) rk[i] = x[i];
    int h = 0;
    for(int i = 1; i <= t; i++) {
        if(rk[i] == 1) continue;
        if(h) h--;
        int j = sa[rk[i] - 1];
        while(j + h <= t && s[i + h] == s[j + h]) h++;
        ht[rk[i]] = h;
    }
}
int ans[55][55];
int bel[maxn];
int pre[55];
int main() {
    n = in();
    len[0] = -1;
    for(int i = 1; i <= n; i++) {
        char ch;
        while(!isalpha(ch = getchar()));
        for(len[i] = len [i - 1] + 1, s[++len[i]] = ch, bel[len[i]] = i; isalpha(ch = getchar()); s[++len[i]] = ch, bel[len[i]] = i);
        s[len[i] + 1] = 300 + i;
    }
    SA();
    for(int i = 2; i <= t; i++) {
        for(int j = 1; j <= n; j++) pre[j] = std::min(pre[j], ht[i]);
        int b = bel[sa[i]];
        pre[bel[sa[i - 1]]] = ht[i];
        if(!b) continue;
        for(int j = 1; j <= n; j++) ans[j][b] = ans[b][j] = std::max(ans[b][j], pre[j]);
    }
    for(int i = 1; i <= n; i++) {
        for(int j = 1; j <= n; j++) {
            if(i == j) continue;
            printf("%d ", ans[i][j]);
        }
        puts("");
    }
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/olinr/p/10254835.html
p
P2P