[bzoj1892][bzoj2384][bzoj1461][Ceoi2011]Match/字符串的匹配_KMP_树状数组
2384: [Ceoi2011]Match 1892: Match 1461: 字符串的匹配
题目大意:
数据范围:
题解:
很巧妙的一道题呀。
需要对$KMP$算法有很深的理解才行。
首先我们需要发现,要求的这个东西跟字符串匹配有点像。
我们在单个模式串匹配的时候用到的$KMP$算法,合法匹配条件是两个字符完全相同。
但是这个题本质上就是要求子串离散化之后相同。
如果两个串离散化之后完全相同,等价于一个条件,就是每个数前面比它小的个数通通相等。
这是显然的。
所以我们尝试改变$KMP$的匹配模式,并且用树状数组维护长串的这个值。
先假设,所有数字两两不同。
对于要求离散化后的串,每个位置弄一个$f_i$表示这个串中,第$i$个位置前面有多少个比$b_i$小的。
我们把如图红色位置加入树状数组
然后我们查询$i$位置,有多少比$a_i$小的,跟$f_{nxt[i-1]}$相比。
如果相等表示这个位置可以匹配,如果不能,我们就把
$i-nxt_{i-1}$到$i-nxt_{nxt_{i - 1}}$。
这样就可以了。
如果离散化之后不完全相等的话,我们就考虑维护出来$i$前面和$b_i$相等的有多少个,再查就行了。
代码:
#include <bits/stdc++.h> #define N 1000010 using namespace std; int tree[N], a[N], b[N], c[N], rk[N], bfr[N], nxt[N], ans[N]; int n, m; char *p1, *p2, buf[100000]; #define nc() (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 100000, stdin), p1 == p2) ? EOF : *p1 ++ ) int rd() { int x = 0, f = 1; char c = nc(); while (c < 48) { if (c == '-') f = -1; c = nc(); } while (c > 47) { x = (((x << 2) + x) << 1) + (c ^ 48), c = nc(); } return x * f; } inline int lowbit(int x) { return x & (-x); } void update(int x, int val) { for (int i = x; i <= m; i += lowbit(i)) tree[i] += val; } int query(int x) { int ans = 0; for (int i = x; i; i -= lowbit(i)) ans += tree[i]; return ans; } int main() { n = rd(), m = rd(); for (int i = 1; i <= n; i ++ ) a[i] = rd(), rk[a[i]] = i; for (int i = 1; i <= n; i ++ ) bfr[i] = query(rk[i]), update(rk[i], 1); for (int i = 1; i <= m; i ++ ) b[i] = rd(), c[i] = b[i]; memset(tree, 0, sizeof tree); // for (int i = 1; i <= n; i ++ ) // printf("%d ", bfr[i]); // puts(""); for (int i = 2, j = 0; i <= n; i ++ ) { while (query(rk[i]) != bfr[j + 1]) { for (int k = i - j; k < i - nxt[j]; k ++ ) update(rk[k], -1); j = nxt[j]; } if (query(rk[i]) == bfr[j + 1]) { update(rk[i], 1); j ++ ; } nxt[i] = j; } // for (int i = 1; i <= n; i ++ ) { // printf("%d ", nxt[i]); // } // puts(""); sort(c + 1, c + m + 1); memset(tree, 0, sizeof tree); for (int i = 1, j = 0; i <= m; i ++ ) { // printf("i-> %d\n", i); b[i] = lower_bound(c + 1, c + m + 1, b[i]) - c; // printf("%d\n", b[i]); // printf("%d %d %d\n", j, query(b[i]), bfr[j + 1]); while (j == n || query(b[i]) != bfr[j + 1]) { for (int k = i - j; k < i - nxt[j]; k ++ ) { update(b[k], -1); } j = nxt[j]; } if (query(b[i]) == bfr[j + 1]) { update(b[i], 1); j ++ ; } if(j == n) ans[ ++ ans[0]] = i - j + 1; } printf("%d\n", ans[0]); for (int i = 1; i < ans[0]; i ++ ) printf("%d ",ans[i]); if(ans[0]) printf("%d\n", ans[ans[0]]); return 0; }
小结:好题啊,这个题真的不好想,我看题解都看了半天.......
| 欢迎来原网站坐坐! >原文链接<