@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. 【python之路26】字符串之格式化%和format

    Python基础之杂货铺   字符串格式化 Python的字符串格式化有两种方式: 百分号方式.format方式 百分号的方式相对来说比较老,而format方式则是比较先进的方式,企图替换古老的方式, ...

  2. 一探前端开发中的JS调试技巧(转)

    有请提示:文中涉及较多Gif演示动画,移动端请尽量在Wifi环境中阅读 前言:调试技巧,在任何一项技术研发中都可谓是必不可少的技能.掌握各种调试技巧,必定能在工作中起到事半功倍的效果.譬如,快速定位问 ...

  3. org.openqa.selenium.ElementNotInteractableException: element not interactable

    F12查看元素确实存在的 有人说延长加载时间 webDriver.manage().timeouts().implicitlyWait(, TimeUnit.SECONDS); // 等待5秒加载完成 ...

  4. git diff 笔记

    有一个 lab1 一个lab2 lab2 是比lab1 新的版本 但是Lab1 中有一些写的新代码,想要保留到lab2 中 直接使用patch 会把 lab2 回退到lab1 或lab1 更新到lab ...

  5. JQ效果 透明图片覆盖动画

    效果图呈上 先说思路 1,一个固定的框架,有两张图片,一张是狗狗的,一张是练习方式,想把做好的练习方式隐藏 2,效果上想要从下面滑动出来,所以透明框定位在下面 3,整理需要的东西,缓慢升起需要动画效果 ...

  6. git之操作准则

    每天下班前合一次代码,每次合代码先pull 不要多人同时修改同一个文件,避免冲突 在每个人自己的分支进行开发,先合并到dev分支解决冲突,确认无冲突后再合并到master

  7. npm ci命令比npm installer命令快2至10倍

    npm 5.7.1的发布给我们带了一系列新的功能. 其中我最喜欢的就是npm ci命令了. npm ci命令 1.npm ci命令只根据lock-file去下载node_modules. 如果你的pa ...

  8. 光(mirror room)

    /* 光线只有遇上边界或堵塞的格子才会改变方向,所以改变方向的位置是有限的,光线的方向又最多只有四种,所以光线在循环之前改变方向的次数是O(n+m+k)级别的.我们可以模拟光线的移动.已知光线位置和光 ...

  9. Eight HDU-1043 (bfs)

    Eight Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total Subm ...

  10. DirectX11笔记(二)--Direct3D初始化1之基本概念

    原文:DirectX11笔记(二)--Direct3D初始化1之基本概念 版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/u010333737/art ...