POJ 1743 Musical Theme

题目大意:

给出N个数, 相邻两数做差之后, 得到N - 1个数, 求一个最大的M, 使得存在两个长为M的子串完全相同, 且两个字串不能有重叠的部分(其实还要再错开一位).

 

简要分析:

记得在USACO中做过这道题. 标准做法当然是后缀数组, 但我用哈希做的.

明显答案可以二分, 二分答案M之后, 从左到右维护长为M的子串的哈希值, 并记录某个哈希值最早出现的下标位置, 这个可以用map搞. 于是就完了. 时间复杂度O(NlogN). 哈希值的算法就很多了, 只需满足一个需求, 就是在已有1..M这个子串的哈希的情况下, 能O(1)得到2..M+1的哈希值. 我用的是大质数幂取模法, 因为C语言的unsigned long long乘爆之后自动对上界取模了, 于是世界就和谐了.

PS: 用STL的map还要TLE, 于是手写hash_map, 详见代码.

 

代码实现:

View Code
 1 #include <cstdio>
2 #include <cstdlib>
3 #include <cstring>
4 #include <algorithm>
5 using namespace std;
6
7 typedef unsigned long long hash_t;
8 const int MAX_N = 20000, SEED = 99991;
9 int n, a[MAX_N];
10 hash_t b[MAX_N - 1], mypow[MAX_N];
11
12 struct hash_map_t {
13 static const int MOD = 196613;
14 int cnt, begin[MOD], val[MAX_N * 2], next[MAX_N * 2];
15 hash_t end[MAX_N * 2];
16 void clear() { cnt = 0, memset(begin, -1, sizeof(begin)); }
17 hash_map_t() { cnt = 0, memset(begin, -1, sizeof(begin)); }
18 void push(hash_t x, int p) {
19 int s = x % MOD;
20 next[cnt] = begin[s];
21 begin[s] = cnt;
22 end[cnt] = x;
23 val[cnt ++] = p;
24 }
25 int ask(hash_t x) {
26 int s = x % MOD;
27 for (int now = begin[s]; now != -1; now = next[now])
28 if (end[now] == x) return val[now];
29 return -1;
30 }
31 } hash;
32
33 bool check(int x) {
34 if (x < 5) return 0;
35 int len = x - 1;
36 hash_t t = 0ULL;
37 for (int i = 0; i < len; i ++) t = t * SEED + b[i];
38 hash.clear();
39 hash.push(t, 0);
40 for (int i = 1; i < n - len + 1; i ++) {
41 t -= b[i - 1] * mypow[len - 1];
42 t = t * SEED + b[i + len - 1];
43 int p = hash.ask(t);
44 if (p < 0) hash.push(t, i);
45 else {
46 p += len;
47 if (i > p) return 1;
48 }
49 }
50 return 0;
51 }
52
53 int main() {
54 mypow[0] = 1ULL;
55 for (int i = 1; i < MAX_N; i ++) mypow[i] = mypow[i - 1] * SEED;
56 while (scanf("%d", &n) != EOF && n) {
57 for (int i = 0; i < n; i ++) scanf("%d", &a[i]);
58 for (int i = 0; i < n - 1; i ++) b[i] = a[i + 1] - a[i];
59 int lb = 5, rb = n + 1;
60 n --;
61 while (lb + 1 < rb) {
62 int mid = (lb + rb) >> 1;
63 if (check(mid)) lb = mid;
64 else rb = mid;
65 }
66 if (!check(lb)) printf("0\n");
67 else printf("%d\n", lb);
68 }
69 return 0;
70 }
posted @ 2012-03-09 22:24  zcwwzdjn  阅读(327)  评论(0编辑  收藏  举报