POJ 2758 Checking the Text
Posted in PKU OJ on 四月 22nd, 2010 by 纳米 – Be the first to comment2010-04-16(17?),阅读题目、题解
2010-04-18、19、20,代码实现,发现理解错题
2010-04-21,代码改写
2010-04-22,发现错误,再改,AC
这道题挺难的,其实也不是很难,就是很麻烦,做完再看,其实也一般般了,不过拖了那么长时间,估计在比赛遇到我打死也不会写这样的题目……
解题报告:
1. 首先需要理解好题目的意思,插入操作的位置是对应当前位置的,而查询操作的位置是对应原始位置的,我就是在这里看错题,发现相当恶心。本来打算把所有的位置转换成原始位置处理,发现不好做,于是统一储存和利用当前位置来计算。
2. 如果没有插入操作,就是经典的LCP问题,用O(n log2 n)的后缀数组,O(n log2 n) – O(n)的RMQ即可顺利解决,于是迅速敲出后缀数组和RMQ。
3. 但是有插入操作,按照POJ的解题报告,视作插入原字符串的空隙即可。好了,所有的麻烦就在这里了。大概这样做(w0, w1为两个位置指针):
(1) 判断要比较的两个位置是否为空隙(or),是转(2),否转(3);
(2) 如果对应的两个字符相同,则ans++、w0++、w1++,转(1),否则跳出;
(3) 计算l = LCP,然后与下一个空隙比较,如果[w0, w0 + l)和[w1, w1 + l)其中一个有空隙,则把l缩小到相应的位置,且ans、w0、w1均增加l;若不存在空隙,ans增加l,并跳出。
4. 时间复杂度分析:设总长度为L,插入操作数A,查询操作数B:后缀数组:O(L log2 L),RMQ初始化:O(L log2 L),插入操作:O(A*A),查询操作:O(A*B)。综上,总时间复杂度为O(L log2 L + A^2 + AB)。比POJ给出解题报告略低,但实现效果一般,2500+ms。
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 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 | #include <stdio.h> #include <string.h> const int maxn = 50205, maxm = 20203, maxins = 203, maxlogn = 16; int n, m, operate[maxm][3], sa[maxn], rank[maxn], height[maxn]; int rmq_arr[maxlogn][maxn], pow[maxlogn], fpow[maxn], insert[maxins][2], insert_n; char words[maxn]; int input() { scanf("%s%d", words, &m); for (int i = 1; i <= m; ++i) { char ch = 0; while (ch != 'Q' && ch != 'I') scanf("%c", &ch); if (ch == 'Q') { operate[i][0] = 0; scanf("%d%d", &operate[i][1], &operate[i][2]); --operate[i][2], --operate[i][1]; } else { operate[i][0] = 1; ch = 0; while ((ch < 'A' || ch > 'Z') && (ch < 'a' || ch > 'z')) scanf("%c", &ch); operate[i][1] = ch; scanf("%d", &operate[i][2]); --operate[i][2]; } } n = strlen(words); return 0; } int build_suffix() { int st[maxn], arr1[maxn], arr2[maxn]; int m = 'z', *sy = arr1, *ra = arr2; memset(st, 0, sizeof(st)); for (int i = 0; i <= n; ++i) ++st[(int)words[i]]; for (int i = 1; i <= m; ++i) st[i] += st[i - 1]; for (int i = n; i >= 0; --i) sa[--st[(int)words[i]]] = i; ra[sa[0]] = m = 0; for (int i = 1; i <= n; ++i) ra[sa[i]] = words[sa[i]] == words[sa[i-1]] ? m : ++m; for (int l = 1; m < n; l += l) { int j = 0, *t; for (int i = n+1-l; i <= n; ++i) sy[j++] = i; for (int i = 0; i <= n; ++i) if (sa[i] >= l) sy[j++] = sa[i] - l; memset(st, 0, sizeof(st)); for (int i = 0; i <= n; ++i) ++st[ra[i]]; for (int i = 1; i <= m; ++i) st[i] += st[i - 1]; for (int i = n; i >= 0; --i) sa[--st[ra[sy[i]]]] = sy[i]; sy[sa[0]] = m = 0; for (int i = 1; i <= n; ++i) sy[sa[i]] = ra[sa[i]]==ra[sa[i-1]] && ra[sa[i]+l]==ra[sa[i-1]+l] ? m : ++m; t = sy; sy = ra; ra = t; } memcpy(rank, ra, sizeof(rank)); return 0; } int calc_height() { for (int i = 0, p = 0; i < n; ++i, p?--p:0) { int j = sa[rank[i]-1]; while (words[i + p] == words[j + p]) ++p; height[rank[i]] = p; } return 0; } int min(int x, int y) { return x < y ? x : y; } int calc_rmq() { for (int i = 1; i <= n; ++i) rmq_arr[0][i] = height[i]; for (int i = 1, l = 1; l < n; ++i, l += l) { for (int j = 1; j + l <= n; ++j) rmq_arr[i][j] = min(rmq_arr[i-1][j], rmq_arr[i-1][j+l]); for (int j = n-l+1; j <= n; ++j) rmq_arr[i][j] = rmq_arr[i-1][j]; } return 0; } int calc_fpow() { int pow0 = 1, pow1 = 2, pows = 0; fpow[1] = 0; do { for (int i = pow0 + 1; i <= pow1; ++i) fpow[i] = pows; pow[pows++] = pow0; pow0 = pow1; pow1 = min(n, pow0 << 1); } while (pow0 < n); return 0; } int rmq(int left, int right) { int k = fpow[right - left + 1]; return min(rmq_arr[k][left], rmq_arr[k][right - pow[k] + 1]); } int lcp(int w0, int w1) { return rank[w0] < rank[w1] ? rmq(rank[w0]+1, rank[w1]) : rmq(rank[w1]+1, rank[w0]); } int nowpos(int key) { int w = 0; while (w < insert_n && insert[w+1][0] - w <= key) ++w; return key + w; } int find_query(int p, int key) { int res = p; while (res < insert_n && insert[res+1][0] <= key) ++res; return res; } int work_query(int w0, int w1) { if (w0 == w1) return n + insert_n - w0; int p0 = 0, p1 = 0, c0, c1; int res = 0, l; while (1) { p0 = find_query(p0, w0); p1 = find_query(p1, w1); if (insert[p0][0] == w0 || insert[p1][0] == w1) { c0 = insert[p0][0] == w0 ? insert[p0][1] : (int)words[w0 - p0]; c1 = insert[p1][0] == w1 ? insert[p1][1] : (int)words[w1 - p1]; if (c0 == c1) { ++w0, ++w1; ++res; } else { break; } } else { l = lcp(w0 - p0, w1 - p1); if (p0 < insert_n) l = min(l, insert[p0+1][0] - w0); if (p1 < insert_n) l = min(l, insert[p1+1][0] - w1); if (l > 0) { w0 += l; w1 += l; res += l; } else { break; } } } return res; } int find_insert(int p, int key) { int res = 0; while (res < insert_n && insert[res+1][0] < key) ++res; return res; } int work_insert(int w, int ch) { int p = find_insert(0, w) + 1; memmove(insert[p+1], insert[p], (int)insert[insert_n+1] - (int)insert[p]); ++insert_n; insert[p][0] = w; insert[p][1] = ch; for (int j = p+1; j <= insert_n; ++j) ++insert[j][0]; return 0; } int work() { insert[0][0] = insert[1][0] = -1; for (int i = 1; i <= m; ++i) if (operate[i][0] == 0) { printf("%d\n", work_query(nowpos(operate[i][1]), nowpos(operate[i][2]))); } else { work_insert(operate[i][2], (int)operate[i][1]); } return 0; } int main() { input(); build_suffix(); calc_height(); calc_rmq(); calc_fpow(); work(); return 0; } |