题解 P4696 【[CEOI2011]Matching】
RedreamMer · · 题解
更好的体验
标签:KMP,链表结构
前言
我靠网上怎么全是带
似乎常数比较小。
题意
不做赘述,注意给定的序列
思路
子串匹配问题,输出方案,哇这限制条件,这好
我们就先向 KMP 方向考虑,注意到输入序列
我们考虑一个成功的匹配是怎样的,即序列
那我们如何重新定义这个 “匹配” 和 “一致”,如何在 KMP 建立正确的 next 数组中进行最优的比较操作?
在建立 next 的数组的过程中,当我们考虑到下标
到这里,是可以用树状数组做了。
对于
而我们只需要求得模式串的前驱和后继即可,将其拿来比较即可。
(这里的前驱和后继是指值小于/大于某个数,且下标小于某个数)
代码
-O2 854ms 目前最优解。
int a, b, s[2][N + 5], L[N + 5], R[N + 5], nxt[N + 5], p[N + 5], l[N + 5], r[N + 5];
vector<int> ans;
bool check(int o, int i, int j, int mn, int mx) {
int res = 1;
if (mn) res &= (s[o][i - (j - mn)] < s[o][i]);
if (mx) res &= (s[o][i - (j - mx)] > s[o][i]);
return res;
}
void init() {
rep(i, 1, a) l[i] = p[s[0][i] - 1], r[i] = p[s[0][i] + 1];
per(i, a, 1) {
L[i] = l[i];
R[i] = r[i];
l[r[i]] = l[i];
r[l[i]] = r[i];
}
int j = 0;
rep(i, 2, a) {
while (j && !check(0, i, j + 1, L[j + 1], R[j + 1])) j = nxt[j];
if (check(0, i, j + 1, L[j + 1], R[j + 1])) ++j;
nxt[i] = j;
}
}
signed main() {
// freopen("in1.in", "r", stdin);
// freopen("out.out", "w", stdout);
a = read();
b = read();
rep(i, 1, a) p[i] = read();
rep(i, 1, a) s[0][p[i]] = i;
rep(i, 1, b) s[1][i] = read();
init();
int j = 0;
rep(i, 1, b) {
while (j && !check(1, i, j + 1, L[j + 1], R[j + 1])) j = nxt[j];
if (check(1, i, j + 1, L[j + 1], R[j + 1])) ++j;
if (j == a) ans.PB(i - a + 1), j = nxt[j];
}
printf("%d\n", siz(ans));
rep(i, 0, siz(ans) - 1) printf("%d ", ans[i]);
return 0;
}