bzoj 4698 Sdoi2008 Sandy的卡片 KMP/后缀数组

题面

题目传送门

解法

KMP大法吼啊
后缀数组当然也能做,待更

  • 根据题意,两个字符串相等当且仅当长度相同并且一个字符串中所有数+ x =另一个串中对应的数
  • 那么,我们可以发现,这两个串相同一定满足这两个串的差分数组相同
  • 所以,我们可以将原来的 n 个字符串变成 n 个长度为原来长度-1的新字符串
  • 现在,问题就转化成要求这 n 个串的最长公共子串
  • 不妨枚举第一个串的起点,将它对应的一段后缀和其他所有字符串做一遍匹配,最后匹配下来的最小值就是最长公共子串的长度
  • 直接用KMP算法加速这个过程即可,代码简单易懂
  • 时间复杂度: O ( n m 2 )

代码

#include <bits/stdc++.h>
#define N 1010
#define M 110
using namespace std;
template <typename node> void chkmax(node &x, node y) {x = max(x, y);}
template <typename node> void chkmin(node &x, node y) {x = min(x, y);}
template <typename node> void read(node &x) {
    x = 0; int f = 1; char c = getchar();
    while (!isdigit(c)) {if (c == '-') f = -1; c = getchar();}
    while (isdigit(c)) x = x * 10 + c - '0', c = getchar(); x *= f;
}
int len, c[M], nxt[M], l[N], a[N][M], b[N][M];
void getnxt(int len) {
    memset(nxt, 0, sizeof(nxt));
    for (int i = 2; i <= len; i++) {
        int j = nxt[i - 1];
        while (j && c[i] != c[j + 1]) j = nxt[j];
        if (c[i] == c[j + 1]) j++; nxt[i] = j;
    }
}
int calc(int num) {
    int L = l[num], ret = 0, j = 0;
    for (int i = 1; i <= L; i++) {
        while (j && a[num][i] != c[j + 1]) j = nxt[j];
        if (a[num][i] == c[j + 1]) j++; chkmax(ret, j);
    }
    return ret;
}
int main() {
    int n; read(n);
    for (int i = 1; i <= n; i++) {
        read(l[i]);
        for (int j = 1; j <= l[i]; j++) read(b[i][j]); l[i]--;
        for (int j = 1; j <= l[i]; j++) a[i][j] = b[i][j + 1] - b[i][j];
    }
    int ret = 0;
    for (int j = 1; j <= l[1]; j++) {
        len = l[1] - j + 1;
        for (int i = 1; i <= len; i++) c[i] = a[1][i + j - 1];
        getnxt(len); int ans = len;
        for (int i = 2; i <= n; i++)
            chkmin(ans, calc(i));
        chkmax(ret, ans + 1);
    }
    cout << ret << "\n";
    return 0;
}

猜你喜欢

转载自blog.csdn.net/emmmmmmmmm/article/details/82083583