【BZOJ4698】【Sdoi2008】Sandy的卡片(kmp后缀数组)

    xiaoxiao2021-03-25  11

    Description

    Sandy和Sue的热衷于收集干脆面中的卡片。然而,Sue收集卡片是因为卡片上漂亮的人物形象,而Sandy则是为了积攒卡片兑换超炫的人物模型。每一张卡片都由一些数字进行标记,第i张卡片的序列长度为Mi,要想兑换人物模型,首先必须要集够N张卡片,对于这N张卡片,如果他们都有一个相同的子串长度为k,则可以兑换一个等级为k的人物模型。相同的定义为:两个子串长度相同且一个串的全部元素加上一个数就会变成另一个串。Sandy的卡片数远小于要求的N,于是Sue决定在Sandy的生日将自己的卡片送给Sandy,在Sue的帮助下,Sandy终于集够了N张卡片,但是,Sandy并不清楚他可以兑换到哪个等级的人物模型,现在,请你帮助Sandy和Sue,看看他们最高能够得到哪个等级的人物模型。

    Input

    第一行为一个数N,表示可以兑换人物模型最少需要的卡片数,即Sandy现在有的卡片数第i+1行到第i+N行每行第一个数为第i张卡片序列的长度Mi,之后j+1到j+1+Mi个数,用空格分隔,分别表示序列中的第j个数 n<=1000,M<=1000,2<=Mi<=101

    Output

    一个数k,表示可以获得的最高等级。

    Sample Input

    2

    2 1 2

    3 4 5 9

    Sample Output

    2

    题解: 对原序列差分并把第一个数除去,所有序列的最长公共子串长度+1就是答案。 用kmp,O(N²M)暴力艹过。

    #include<iostream> #include<stdio.h> #include<algorithm> #include<string.h> #include<math.h> #define ll long long #define inf 0x7f7f7f7f using namespace std; int n,m,t[1005][105],f[105]; void pre(int *t) { int i=0,j=f[0]=-1; while(t[i]) { while(~j && t[i]^t[j]) j=f[j]; f[++i]=++j; } } int solve(int *s,int *t) { int i=0,j=0,k=0; while(s[i]) { while(~j && s[i]^t[j]) j=f[j]; i++,k=max(k,++j); } return k; } int main() { scanf("%d",&n); for(int i=1;i<=n;i++) { int *s=t[i]; scanf("%d",&m); for(int j=0;j<m;j++) scanf("%d",&s[j]); for(int j=m-1;j;j--) s[j]=s[j]-s[j-1]+1e9; } int e=0; for(int j=1;j<=m;j++) { pre(t[n]+j); int v=m-j+1; for(int i=1;i<n;i++) v=min(v,solve(t[i]+1,t[n]+j)); e=max(e,v); } printf("%d\n",e+1); }

    不过用后缀数组+二分好像可以优化到O(NlogN),我没写。

    转载请注明原文地址: https://ju.6miu.com/read-200164.html

    最新回复(0)