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个数
Output:
一个数k,表示可以获得的最高等级。
Sample Input:
1 2 3 | 2 2 1 2 3 4 5 9 |
Sample Output:
1 | 2 |
题解:
直接处理处相邻字符的差,转换为字符串匹配的问题。
然后我们对于第一个字符串枚举起点,然后getfail,对于剩下的n-1个字符串KMP一下,然后就可以了。
其实有更快的后缀数组的算法。
我们把所有的字符串拼到一起,中间用最小的特殊字符隔开,求一遍后缀数组,注意求height的时候不能够越过特殊字符。
然后我们二分最后的匹配长度,如果在height中有连续的一段大于或等于这个二分的长度,且这一段中包含了来自于1~n的字符串的后缀,那么这个答案是可行的。
KMP时间复杂度 \(O(nm^2)\)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 | /* ID: Sunshine_cfbsl PROG: BZOJ4698 LANG: C++ */ #include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int MAXN = 1100; int n, l[MAXN], s[MAXN][MAXN]; int fail[MAXN], a[MAXN], ans; inline int read() { int x = 0, f = 1; char ch = getchar(); for(; ch<'0' || ch>'9'; ch=getchar()) if(ch=='-') f=-1; for(; ch>='0'&&ch<='9'; ch=getchar()) x = x*10 + ch-'0'; return x * f; } inline void getfail(int s[], int l) { int i; memset(fail, 0, sizeof(fail)); for(i = 2; i <= l; i++) { int j = fail[i-1]; while(j > 0 && s[j+1] != s[i]) j = fail[j]; if(s[j+1] == s[i]) fail[i] = j+1; else fail[i] = 0; } } int main() { int i, j, q; n = read(); for(i = 1; i <= n; i++) { l[i] = read(); for(j = 1; j <= l[i]; j++) a[j] = read(); for(j = 1; j < l[i]; j++) s[i][j] = a[j+1]-a[j]; l[i]--; } for(i = 1; i <= l[1]; i++) { getfail(s[1]+i-1, l[1]); int *T = s[1]+i-1, res = l[1]-i+1; for(j = 2; j <= n; j++) { int p = 0, len = 0; for(q = 1; q <= l[j]; q++) { while(p > 0 && s[j][q] != T[p+1]) p = fail[p]; if(s[j][q] == T[p+1]) p++, len = max(p, len); } res = min(res, len); } ans = max(ans, res+1); } printf("%d\n", ans); return 0; } |
后缀数组时间复杂度 \(O(n \times m\ log_2(m\times n))\) (当然线性构造会降到 \(O(n\times m)\) )
代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 | /* ID: Sunshine_cfbsl PROG: BZOJ4698 LANG: C++ */ #include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int MAXN = 1100, INF = 1000000000; int n, l, t[MAXN], top; int x[MAXN*MAXN], y[MAXN*MAXN], stk[MAXN]; int id[MAXN*MAXN], s[MAXN*MAXN]; int ans, len, buc[MAXN*MAXN], sa[MAXN*MAXN]; int rnk[MAXN*MAXN], height[MAXN*MAXN]; bool vis[MAXN]; inline int read() { int x = 0, f = 1; char ch = getchar(); for(; ch<'0' || ch>'9'; ch=getchar()) if(ch=='-') f=-1; for(; ch>='0'&&ch<='9'; ch=getchar()) x = x*10 + ch-'0'; return x * f; } inline void build(int s[], int n) { int i, k, m = 1000010; for(i = 0; i < n; i++) buc[x[i] = s[i]]++; for(i = 1; i < m; i++) buc[i] += buc[i-1]; for(i = n-1; i >= 0; i--) sa[--buc[x[i]]] = i; for(k = 1; k <= n; k <<= 1) { int p = -1; for(i = n-1; i >= n-k; i--) y[++p] = i; for(i = 0; i < n; i++) if(sa[i] >= k) y[++p] = sa[i]-k; for(i = 0; i < m; i++) buc[i] = 0; for(i = 0; i < n; i++) buc[x[y[i]]]++; for(i = 1; i < m; i++) buc[i] += buc[i-1]; for(i = n-1; i >= 0; i--) sa[--buc[x[y[i]]]] = y[i]; swap(x, y), p = 0, x[sa[0]] = 0; for(i = 1; i < n; i++) { p += (y[sa[i]] != y[sa[i-1]] || y[sa[i]+k] != y[sa[i-1]+k]); x[sa[i]] = p; } if((m = p+1) >= n) break; } for(i = 0; i < n; i++) rnk[sa[i]] = i; for(k = 0, i = 0; i < n; i++) { if(!rnk[i]) continue; if(k) k--; int j = sa[rnk[i]-1]; while(s[i+k] == s[j+k] && s[i+k] != 0 && i+k<n && j+k<n) k++; height[rnk[i]] = k; } } bool check(int l) { int i, c = 0; for(i = 1; i < len; i++) { if(id[sa[i-1]] == 0) continue; if(id[sa[i]] == 0) continue; if(height[i] < l) { c = 0; while(top) vis[stk[top--]] = false; } else { int u = id[sa[i-1]], v = id[sa[i]]; if(!vis[u]) vis[u] = true, stk[++top] = u, c++; if(!vis[v]) vis[v] = true, stk[++top] = v, c++; } if(c == n) { while(top) vis[stk[top--]] = false; return true; } } while(top) vis[stk[top--]] = false; return false; } int main() { int i, j, Min = INF; n = read(); for(i = 1; i <= n; i++) { l = read(); for(j = 1; j <= l; j++) t[j] = read(); for(j = 1; j < l; j++) { s[len++] = t[j+1]-t[j]; id[len-1] = i; } s[len++] = INF; id[len-1] = 0; } for(i = 0; i < len; i++) if(s[i] != INF) Min = min(Min, s[i]); for(i = 0; i < len; i++) { if(s[i] != INF) s[i] -= (Min-1); else s[i] = 0; } build(s, len); int L = 0, R = n; while(L < R) { int mid = (L+R+1)>>1; if(check(mid)) L = mid; else R = mid-1; } printf("%d\n", L+1); return 0; } |