[BZOJ4698][Sdoi2008]Sandy的卡片
[BZOJ4698][Sdoi2008]Sandy的卡片
试题描述
Sandy和Sue的热衷于收集干脆面中的卡片。然而,Sue收集卡片是因为卡片上漂亮的人物形象,而Sandy则是为了积攒卡片兑换超炫的人物模型。每一张卡片都由一些数字进行标记,第i张卡片的序列长度为Mi,要想兑换人物模型,首先必须要集够N张卡片,对于这N张卡片,如果他们都有一个相同的子串长度为k,则可以兑换一个等级为k的人物模型。相同的定义为:两个子串长度相同且一个串的全部元素加上一个数就会变成另一个串。Sandy的卡片数远远小于要求的N,于是Sue决定在Sandy的生日将自己的卡片送给Sandy,在Sue的帮助下,Sandy终于集够了N张卡片,但是,Sandy并不清楚他可以兑换到哪个等级的人物模型,现在,请你帮助Sandy和Sue,看看他们最高能够得到哪个等级的人物模型。
输入
第一行为一个数N,表示可以兑换人物模型最少需要的卡片数,即Sandy现在有的卡片数
第i+1行到第i+N行每行第一个数为第i张卡片序列的长度Mi,之后j+1到j+Mi个数,用空格分隔,分别表示序列中
的第j个数
N<=1000,2<=Mi<=101
输出
一个数k,表示可以获得的最高等级。
输入示例
2 2 1 2 3 4 5 9
输出示例
2
数据规模及约定
见“输入”
题解
首先把所有串差分一下;再把第一个串的每个后缀分别当成模板串依次对第 2~n 个串跑 KMP,找到能够匹配所有第 2~n 个串的最长子串。
#include <iostream> #include <cstdio> #include <cstdlib> #include <cstring> #include <cctype> #include <algorithm> using namespace std; int read() { int x = 0, f = 1; char c = getchar(); while(!isdigit(c)){ if(c == '-') f = -1; c = getchar(); } while(isdigit(c)){ x = x * 10 + c - '0'; c = getchar(); } return x * f; } #define maxn 1010 #define maxl 110 int n, len[maxn], S[maxn][maxl], Fail[maxl]; int main() { n = read(); for(int i = 1; i <= n; i++) { len[i] = read(); for(int j = 1; j <= len[i]; j++) S[i][j] = read(); len[i]--; for(int j = 1; j <= len[i]; j++) S[i][j] = S[i][j+1] - S[i][j]; } /*for(int i = 1; i <= n; i++) for(int j = 1; j <= len[i]; j++) printf("%d%c", S[i][j], j < len[i] ? ' ' : '\n'); // */ int ans = 0; for(int st = 1; st <= len[1] - ans; st++) { int nl = len[1] - st + 1; for(int i = 2; i <= nl + 1; i++) { int j = Fail[i-1]; while(j > 1 && S[1][j+st-1] != S[1][i+st-2]) j = Fail[j]; Fail[i] = S[1][j+st-1] == S[1][i+st-2] ? j + 1 : 1; } int p, k = len[1]; for(int x = 2; x <= n; x++) { p = 1; int tmp = 0; for(int i = 1; i <= len[x]; i++) { while(p > 1 && S[1][p+st-1] != S[x][i]) p = Fail[p]; p = S[1][p+st-1] == S[x][i] ? p + 1 : 1; tmp = max(tmp, p - 1); } k = min(k, tmp); if(k <= ans) break; } ans = max(ans, k); } printf("%d\n", ans + 1); return 0; }