@description@

对于整数序列 \((a_1, a_2, ..., a_n)\) 和 1 ~ n 的排列 \((p_1, p_2, ..., p_n)\),称 \((a_1, a_2, ..., a_n)\) 符合 \((p_1, p_2, ..., p_n)\),当且仅当:

(1){a} 中任意两个数字互不相同。

(2)将 \((a_1, a_2, ..., a_n)\) 从小到大排序后,将会得到 \((a_{p_1}, a_{p_2}, ..., a_{p_n})\)。

现在给出 1 ~ n 的排列 {p} 与序列 \(h_1, h_2, ..., h_m\),请你求出哪些 h 的子串符合排列 {p}。

输入格式

第一行两个空格隔开的正整数 n, m。

第二行 n 个空格隔开的正整数,表示排列 p。

第三行 m 个空格隔开的正整数,表示序列 h。

输出格式

第一行一个整数 k,表示符合 {p} 的子串个数。

第二行 k 个空格隔开的正整数,表示这些子串的起始位置(编号从 1 开始)。请将这些位置按照从小到大的顺序输出。特别地,若 k = 0,那么你也应当输出一个空行。

样例输入

5 10

2 1 5 3 4

5 6 3 8 12 7 1 10 11 9

样例输出

2

2 6

数据范围与提示

2 <= n <= m <= 1000000; 1 <= hi <= 10^9; 1 <= pi <= n。

且保证 {h} 中元素互不相同,{p} 是一个排列。

@solution@

先对问题作一步转化:求 {q} 使得 \(q_{p_i} = i\),即 {p} 的逆置换。

那么某个子串符合 {p} 可以等价于这个子串离散化到 1 ~ n 中后等于 q。

如果不考虑离散化,那么就是一个经典子串匹配问题,直接上 kmp。

假如子串同构的判定方法如上,即离散化后同构,是否还可以扩展一下 kmp 呢?

考虑 kmp 什么时候需要判同构:已知串 s 与串 t 同构时,在 s 末尾加一个 a,在 t 末尾加一个 b,判断 s + a 与 t + b 是否同构。

因为 s 与 t 已经同构了,只需要 a 与 b 加入进去过后仍然同构即可。

可以等价于判定 a 在 s 中的排名(s 中比 a 小的数) = b 在 t 中的排名(t 中比 b 小的数)。

查排名可以平衡树,不过这道题直接离散化 + 树状数组即可。

注意 kmp 在跳 fail 的时候,需要一个个元素的移动,因为要维护树状数组。

不过复杂度的证明是不会变的。kmp 还是 O(n),套个树状数组就是 O(nlogn) 的。

@accepted code@

#include <cstdio>
#include <vector>
#include <algorithm>
using namespace std;
const int MAXN = 1000000;
int n, m;
int t[2][MAXN + 5];
int lowbit(int x) {return x & -x;}
void update(int x, int k, int type) {
for(int i=x;i<=m;i+=lowbit(i))
t[type][i] += k;
}
int sum(int x, int type) {
int ret = 0;
for(int i=x;i;i-=lowbit(i))
ret += t[type][i];
return ret;
}
int d[MAXN + 5], p[MAXN + 5], h[MAXN + 5];
void discrete() {
for(int i=1;i<=m;i++) d[i] = h[i];
sort(d + 1, d + m + 1);
for(int i=1;i<=m;i++)
h[i] = lower_bound(d + 1, d + m + 1, h[i]) - d;
}
int f[MAXN + 5];
void get_f() {
f[1] = 0;
int ri = 0, le = 2;
for(int i=2;i<=n;i++) {
int j = f[i-1];
while( sum(p[j+1], 0) != sum(p[i], 1) ) {
while( ri != f[j] )
update(p[ri--], -1, 0), update(p[le++], -1, 1);
j = f[j];
}
f[i] = j + 1;
update(p[++ri], 1, 0), update(p[i], 1, 1);
}
}
vector<int>ans;
void get_ans() {
for(int i=1;i<=m;i++)
t[0][i] = t[1][i] = 0;
int le = 1, ri = 0, j = 0;
for(int i=1;i<=m;i++) {
while( sum(p[j+1], 0) != sum(h[i], 1) ) {
while( ri != f[j] )
update(p[ri--], -1, 0), update(h[le++], -1, 1);
j = f[j];
}
j++;
update(p[++ri], 1, 0), update(h[i], 1, 1);
if( j == n ) {
ans.push_back(i-n+1);
while( ri != f[j] )
update(p[ri--], -1, 0), update(h[le++], -1, 1);
j = f[j];
}
}
}
int main() {
scanf("%d%d", &n, &m);
for(int i=1;i<=n;i++) {
int x; scanf("%d", &x);
p[x] = i;
}
for(int i=1;i<=m;i++) scanf("%d", &h[i]);
discrete(), get_f(), get_ans();
printf("%d\n", (int)ans.size());
for(int i=0;i<(int)ans.size();i++)
printf("%d%c", ans[i], (i + 1 == ans.size() ? '\n' : ' '));
if( ans.empty() ) puts("");
}

@details@

曾经想过字符串 hash,因为这个 hash 直接就是康托展开算。

发现这个题 hash 并不能前缀和相减,动态维护感觉还要写平衡树,于是放弃了。

毕竟相对于平衡树大家还是喜欢代码简短的树状数组吧。

@loj - 2507@ 「CEOI2011」Matching的更多相关文章

  1. 【LOJ#2507】[CEOI2011]Matching(KMP,树状数组)

    [LOJ#2507][CEOI2011]Matching(KMP,树状数组) 题面 LOJ 题解 发现要做的是排名串的匹配. 然后我们考虑把它转成这个位置之前有多少个数小于当前这个数,这样子只要每个位 ...

  2. Loj #2192. 「SHOI2014」概率充电器

    Loj #2192. 「SHOI2014」概率充电器 题目描述 著名的电子产品品牌 SHOI 刚刚发布了引领世界潮流的下一代电子产品--概率充电器: 「采用全新纳米级加工技术,实现元件与导线能否通电完 ...

  3. Loj #3096. 「SNOI2019」数论

    Loj #3096. 「SNOI2019」数论 题目描述 给出正整数 \(P, Q, T\),大小为 \(n\) 的整数集 \(A\) 和大小为 \(m\) 的整数集 \(B\),请你求出: \[ \ ...

  4. Loj #3093. 「BJOI2019」光线

    Loj #3093. 「BJOI2019」光线 题目描述 当一束光打到一层玻璃上时,有一定比例的光会穿过这层玻璃,一定比例的光会被反射回去,剩下的光被玻璃吸收. 设对于任意 \(x\),有 \(x\t ...

  5. Loj #3089. 「BJOI2019」奥术神杖

    Loj #3089. 「BJOI2019」奥术神杖 题目描述 Bezorath 大陆抵抗地灾军团入侵的战争进入了僵持的阶段,世世代代生活在 Bezorath 这片大陆的精灵们开始寻找远古时代诸神遗留的 ...

  6. Loj #2542. 「PKUWC2018」随机游走

    Loj #2542. 「PKUWC2018」随机游走 题目描述 给定一棵 \(n\) 个结点的树,你从点 \(x\) 出发,每次等概率随机选择一条与所在点相邻的边走过去. 有 \(Q\) 次询问,每次 ...

  7. Loj #3059. 「HNOI2019」序列

    Loj #3059. 「HNOI2019」序列 给定一个长度为 \(n\) 的序列 \(A_1, \ldots , A_n\),以及 \(m\) 个操作,每个操作将一个 \(A_i\) 修改为 \(k ...

  8. Loj #3056. 「HNOI2019」多边形

    Loj #3056. 「HNOI2019」多边形 小 R 与小 W 在玩游戏. 他们有一个边数为 \(n\) 的凸多边形,其顶点沿逆时针方向标号依次为 \(1,2,3, \ldots , n\).最开 ...

  9. Loj #3055. 「HNOI2019」JOJO

    Loj #3055. 「HNOI2019」JOJO JOJO 的奇幻冒险是一部非常火的漫画.漫画中的男主角经常喜欢连续喊很多的「欧拉」或者「木大」. 为了防止字太多挡住漫画内容,现在打算在新的漫画中用 ...

随机推荐

  1. TZOJ 4267 An Easy Puz(深搜)

    描述 Wddpdh find an interesting mini-game in the BBS of WHU, called “An easy PUZ”. It’s a 6 * 6 chess ...

  2. 安装office2016时弹出microsoft setup bootstrapper已停止工作的解决办法

    安装office2016时安装进度条走到最后又回滚,弹出microsoft setup bootstrapper已停止工作,最后“安装出错” 经过了1天的试尽了各种控制面板卸载.文件夹删除.offic ...

  3. java中error和exception的区别

    转自:https://www.jianshu.com/p/e8bbee3c1c4a Error类和Exception类的父类都是throwable类,他们的区别是: Error类一般是指与虚拟机相关的 ...

  4. 关于 webpack的总结

    一 . 几个基本的概念 1. mode开发模式 // webpack.production.config.js module.exports = { mode: 'production' // 生产模 ...

  5. web前端学习(四)JavaScript学习笔记部分(8)-- JavaScript 浏览器对象

    1.window对象 1.1.window对象: window对象是BOM的核心,window对象指当前的浏览器窗口 所有javaScript全局对象.函数以及变量均自动生成为window对象的成员 ...

  6. Python子进程 (subprocess包)

    Python子进程 (subprocess包) subprocess以及常用的封装函数 当我们运行python的时候,我们都是在创建并运行一个进程.正如我们在Linux进程基础中介绍的那样,一个进程可 ...

  7. python 变量离散化

  8. 导出excel表

    <?phppublic function export(){          #提现状态               $status = isset($_REQUEST['status'])? ...

  9. ML面试1000题系列(51-60)

    本文总结ML面试常见的问题集 转载来源:https://blog.csdn.net/v_july_v/article/details/78121924 51.简单说下sigmoid激活函数 常用的非线 ...

  10. javascript函数式编程和链式优化

    1.函数式编程理解 函数式编程可以理解为,以函数作为主要载体的编程方式,用函数去拆解.抽象一般的表达式 与命令式相比,这样做的好处在哪?主要有以下几点: (1)语义更加清晰 (2)可复用性更高 (3) ...