luogu 2463 [SDOI2008]Sandy的卡片 kmp || 后缀数组 n个串的最长公共子串

题目链接

Description

给出\(n\)个序列。找出这\(n\)个序列的最长相同子串。

在这里,相同定义为:两个子串长度相同且一个串的全部元素加上一个数就会变成另一个串。

思路

参考:hzwer.

法一:kmp

在第一个串中枚举答案串的开头位置,与其余\(n-1\)个串做\(kmp\).

法二:后缀数组

将\(n\)个串拼接起来。二分答案\(len\),将\(height\)分组,\(check\)是否有一组个数\(\geq len\)且落在\(n\)个不同的串中。

注意:\(n\)个串之间的\(n-1\)分隔符应该使用各不相同的符号。

对“相同”的处理

法一

kmp中,可重新定义“==”的含义。

思想类似 HDU 4749 & POJ 3167 kmp变形

法二:差分

Code

Ver. 1: kmp

#include <bits/stdc++.h>
#define maxn 1010
using namespace std;
typedef long long LL;
int a[maxn][maxn], f[maxn], m[maxn];
bool match(int* T, int* P, int p2, int p1) { return !p1 || T[p2]-T[p2-1]==P[p1]-P[p1-1]; }
void getfail(int* P, int n) {
    f[0] = f[1] = 0;
    for (int i = 1; i < n; ++i) {
        int j = f[i];
        while (j && !match(P, P, i, j)) j = f[j];
        f[i+1] = match(P, P, i, j) ? j+1 : 0;
    }
}
int kmp(int* T, int* P, int m, int n) {
    int j = f[0], ret = 0;
    for (int i = 0; i < m; ++i) {
        while (j && !match(T, P, i, j)) j = f[j];
        if (match(T, P, i, j)) ++j;
        if (j==n) return n;
        ret = max(ret, j);
    }
    return ret;
}
int main() {
    int n;
    scanf("%d", &n);
    for (int i = 0; i < n; ++i) {
        scanf("%d", &m[i]);
        for (int j = 0; j < m[i]; ++j) scanf("%d", &a[i][j]);
    }
    int ans = 0;
    for (int i = 0; i < m[0]; ++i) {
        int len = m[0]-i;
        if (len < ans) break;
        getfail(a[0]+i, len);
        int minn = len;
        for (int j = 1; j < n; ++j) minn = min(minn, kmp(a[j], a[0]+i, m[j], m[0]-i));
        ans = max(ans, minn);
    }
    printf("%d\n", ans);
    return 0;
}

Ver. 2: 后缀数组

#include <bits/stdc++.h>
#define maxn 1010
#define MAXN (int)1e7
using namespace std;
typedef long long LL;
int wa[MAXN], wb[MAXN], wv[MAXN], wt[MAXN], h[MAXN], rk[MAXN], sa[MAXN], n, r[MAXN],
    id[MAXN], l[maxn], a[maxn][maxn], vis[maxn];
bool flag[MAXN];
vector<int> v;
bool cmp(int* r, int a, int b, int l) { return r[a] == r[b] && r[a+l] == r[b+l]; }
void init(int* r, int* sa, int n, int m) {
    int* x=wa, *y=wb, *t, i, j, p;
    for (i = 0; i < m; ++i) wt[i] = 0;
    for (i = 0; i < n; ++i) ++wt[x[i] = r[i]];
    for (i = 1; i < m; ++i) wt[i] += wt[i - 1];
    for (i = n-1; i >= 0; --i) sa[--wt[x[i]]] = i;
    for (j = 1, p = 1; p < n; j <<= 1, m = p) {
        for (p = 0, i = n-j; i < n; ++i) y[p++] = i;
        for (i = 0; i < n; ++i) if (sa[i] >= j) y[p++] = sa[i] - j;
        for (i = 0; i < n; ++i) wv[i] = x[y[i]];
        for (i = 0; i < m; ++i) wt[i] = 0;
        for (i = 0; i < n; ++i) ++wt[wv[i]];
        for (i = 1; i < m; ++i) wt[i] += wt[i - 1];
        for (i = n-1; i >= 0; --i) sa[--wt[wv[i]]] = y[i];
        t = x, x = y, y = t, x[sa[0]] = 0;
        for (p = 1, i = 1; i < n; ++i) x[sa[i]] = cmp(y, sa[i], sa[i-1], j) ? p - 1 : p++;
    }
    for (i = 0; i < n; ++i) rk[sa[i]] = i;
    int k = 0;
    for (i = 0; i < n - 1; h[rk[i++]] = k) {
        for (k = k ? --k : 0, j = sa[rk[i] - 1]; r[i+k] == r[j+k]; ++k);
    }
}
int cur;
bool ok(vector<int>& v) {
    ++cur;
    if (v.size()<n) return false;
    int cnt = 0;
    for (auto x : v) {
        if (flag[x]) continue;
        if (vis[id[x]]!=cur) ++cnt, vis[id[x]] = cur;
    }
    return cnt==n;
}
bool check(int len, int tot) {
    bool cnt=0;
    for (int i = 1; i < tot; ++i) {
        if (h[i] < len) {
            if (cnt && ok(v)) return true;
            v.clear(); cnt = true;
        }
        v.push_back(sa[i]);
    }
    return false;
}
int main() {
    int tot=0, m=0, x, le=1, ri=1010, ans=0;
    scanf("%d", &n);
    for (int i = 0; i < n; ++i) {
        scanf("%d", &l[i]); ri = min(ri, l[i]-1);
        for (int j = 0; j < l[i]; ++j) {
            scanf("%d", &a[i][j]);
            if (j) m = max(m, a[i][j]-a[i][j-1]);
        }
    }
    for (int i = 0; i < n; ++i) {
        for (int j = 1; j < l[i]; ++j) {
            r[tot] = a[i][j]-a[i][j-1];
            id[tot++] = i;
        }
        r[tot] = ++m; id[tot] = i; flag[tot++] = true;
    }
    r[tot-1] = 0;
    int mn = r[0]; for (int i = 1; i < tot-1; ++i) mn = min(r[i], mn);
    for (int i = 0; i < tot-1; ++i) r[i] -= mn-1; m -= mn-1;
    init(r, sa, tot, ++m);
    while (le <= ri) {
        int mid = le+ri>>1;
        if (check(mid, tot)) ans=mid, le=mid+1;
        else ri=mid-1;
    }
    printf("%d\n", ans+1);
    return 0;
}

原文地址:https://www.cnblogs.com/kkkkahlua/p/8445375.html

时间: 2024-08-26 08:30:50

luogu 2463 [SDOI2008]Sandy的卡片 kmp || 后缀数组 n个串的最长公共子串的相关文章

poj2774 后缀数组2个字符串的最长公共子串

Long Long Message Time Limit: 4000MS   Memory Limit: 131072K Total Submissions: 26601   Accepted: 10816 Case Time Limit: 1000MS Description The little cat is majoring in physics in the capital of Byterland. A piece of sad news comes to him these days

【poj1226-出现或反转后出现在每个串的最长公共子串】后缀数组

题意:求n个串的最长公共子串,子串出现在一个串中可以是它的反转串出现.总长<=10^4. 题解: 对于每个串,把反转串也连进去.二分长度,分组,判断每个组. 1 #include<cstdio> 2 #include<cstdlib> 3 #include<cstring> 4 #include<iostream> 5 using namespace std; 6 7 const int N=2*21000; 8 int n,sl,cl,c[N],rk

后缀数组 模板题 hdu1403(最长公共(连续)子串)

好气啊,今天没有看懂后缀树和后缀自动机 只能写个后缀数组发泄一下了orz #include <cstdio> #include <cstring> const int N = 100017*2; int wa[N], wb[N], wv[N], ws[N]; int rank[N]; int height[N]; char str[N]; int s[N], sa[N]; int cmp(int *r, int a, int b, int l) { return r[a]==r[b

POJ 3261 Milk Patterns 后缀数组求 一个串种 最长可重复子串重复至少k次

Milk Patterns Description Farmer John has noticed that the quality of milk given by his cows varies from day to day. On further investigation, he discovered that although he can't predict the quality of milk from one day to the next, there are some r

POJ - 3415 Common Substrings(后缀数组求长度不小于 k 的公共子串的个数+单调栈优化)

Description A substring of a string T is defined as: T( i, k)= TiTi+1... Ti+k-1, 1≤ i≤ i+k-1≤| T|. Given two strings A, B and one integer K, we define S, a set of triples (i, j, k): S = {( i, j, k) | k≥ K, A( i, k)= B( j, k)}. You are to give the val

POJ 题目3294Life Forms(后缀数组求超过k个的串的最长公共子串)

Life Forms Time Limit: 5000MS   Memory Limit: 65536K Total Submissions: 11178   Accepted: 3085 Description You may have wondered why most extraterrestrial life forms resemble humans, differing by superficial traits such as height, colour, wrinkles, e

poj 1743 二分答案+后缀数组 求不重叠的最长重复子串

题意:给出一串序列,求最长的theme长度 (theme:完全重叠的子序列,如1 2 3和1 2 3  or  子序列中每个元素对应的差相等,如1 2 3和7 8 9) 要是没有差相等这个条件那就好办多了,直接裸题. 一开始想了个2B方法,后来发现真心2B啊蛤蛤蛤 1 for i=1 to 88 do 2 { 3 for j=1 to length 4 { 5 r2[j]=r[j]+i; 6 if (r2[j]>88) r2[i]-=88; 7 } 8 把新序列r2连接到原序列r的后面 9 pr

后缀数组(多个字符串的最长公共子串)—— POJ 3294

对应POJ 题目:点击打开链接 Life Forms Time Limit:6666MS     Memory Limit:0KB     64bit IO Format:%lld & %llu Submit Status Description Problem C: Life Forms You may have wondered why most extraterrestrial life forms resemble humans, differing by superficial tra

POJ 2217 (后缀数组+最长公共子串)

题目链接: http://poj.org/problem?id=2217 题目大意: 求两个串的最长公共子串,注意子串是连续的,而子序列可以不连续. 解题思路: 有个炒鸡快的O(n)的Manacher算法.不过只能求裸的最长公共和回文子串. 后缀数组解法是这类问题的模板解法. 对于n个串的最长公共子串,这要把这些串连在一起,中间用"$"这类的特殊符号分隔一下. 先求后缀数组,再求最长公共前缀,取相邻两个且属于不同串的sa的最大LCP即可. 原理就是:这样把分属两个串的LCP都跑了一遍,