题解

好神仙的题啊

感觉转二维平面能想到,算重复情况的方法真想不到啊

通过扒stdcall代码获得的题解QAQQQQ

我们先把\(p_i\)正串反串建出一个AC自动机来

然后我们把s串放在上面跑匹配,正着跑一遍,反着跑一遍,我们就得到了\(s\)中每个位置正着和反着能匹配到的节点编号

然后对于AC自动机,我们建出fail树来,并处理出每个点在fail树上dfs序

对于AC自动机上的一个点,我们把\(p_i\)正串的询问挂在上面,假如这个点的匹配深度为x,那么我们就需要对于这个点fail树里所有匹配到的\(s\)中正着匹配到的位置,问这个位置a,\(a + k + 1\)是不是在\(p_i\)反串\(x + k + 1\)(也是原下标)所在AC自动机节点fail树的子树里

举个例子

k = 3

假如\(p_i\)是

abacdefg

然后s中的一段是

abahdefg

我们找到\(p_i\)在AC自动机中aba所在的节点,挂上\(p_i\)这个询问,找出所有这个节点fail树里匹配到的s正串的位置\(a\)

查询的是\(p_i\)反串gfe所在节点fail树子树里,问\(a + k + 1\)有多少个

这个可以通过树状数组维护,问一个点子树里的只要用遍历子树后的值减掉遍历子树前的值就好

然而……你发现这样统计会有重复了吗

还是刚刚那个例子

我们发现,当我们在a所在的节点时,我们还会计算一遍这一段

为了去重,我们使得左边匹配要到达最长,也就是,如果这个位置\(a + k\)也合法的话,那么我们要减掉它

就是在aba这个点,同时挂上gfed节点,然后减掉这些方案

维护方法还是树状数组,和上面类似

代码

#include <bits/stdc++.h>
#define fi first
#define se second
#define pii pair<int,int>
#define pdi pair<db,int>
#define mp make_pair
#define pb push_back
#define enter putchar('\n')
#define space putchar(' ')
#define eps 1e-8
#define MAXN 200005
#define mo 974711
//#define ivorysi
using namespace std;
typedef long long int64;
typedef double db;
template<class T>
void read(T &res) {
res = 0;char c = getchar();T f = 1;
while(c < '0' || c > '9') {
if(c == '-') f = -1;
c = getchar();
}
while(c >= '0' && c <= '9') {
res = res * 10 + c - '0';
c = getchar();
}
res *= f;
}
template<class T>
void out(T x) {
if(x < 0) {x = -x;putchar('-');}
if(x >= 10) {
out(x / 10);
}
putchar('0' + x % 10);
} int L[MAXN],R[MAXN],K,N,dfn[MAXN * 2],idx,siz[MAXN * 2],H,ans[MAXN],pos[2][MAXN];
char s[MAXN],t[MAXN];
vector<int> son[MAXN * 2];
vector<int> ver[MAXN * 2];
vector<pii > Qry[MAXN * 2];
int sum[2][MAXN * 2];
namespace ACAM {
int tr[MAXN * 2][94],fail[MAXN * 2],Ncnt,rt,que[MAXN * 2],ql,qr;
void Init() {
Ncnt = 1,rt = 1;
}
int ins(int p,int c) {
if(!tr[p][c]) tr[p][c] = ++Ncnt;
return tr[p][c];
}
void build_ACAM() {
ql = 1,qr = 0;
que[++qr] = 1;fail[1] = 1;
while(ql <= qr) {
int u = que[ql++];
for(int i = 0 ; i <= 93 ; ++i) {
if(tr[u][i]) {
int v = tr[u][i];
int t = fail[u];
if(u == 1) fail[v] = 1;
else {
while(1) {
if(tr[t][i]) {fail[v] = tr[t][i];break;}
if(t == 1) {fail[v] = 1;break;}
t = fail[t];
}
}
que[++qr] = v;
son[fail[v]].pb(v);
}
}
}
}
}
using ACAM::ins;
void dfs(int u) {
dfn[u] = ++idx;siz[u] = 1;
int s = son[u].size();
for(int i = 0 ; i < s ; ++i) {
dfs(son[u][i]);
siz[u] += siz[son[u][i]];
}
}
int lowbit(int x) {return x & (-x);}
void insert(int on,int x) {
while(x <= ACAM::Ncnt) {
sum[on][x]++;
x += lowbit(x);
}
}
int Query(int on,int x) {
int res = 0;
while(x > 0) {
res += sum[on][x];
x -= lowbit(x);
}
return res;
}
int Query_Range(int on,int x) {
return Query(on,dfn[x] + siz[x] - 1) - Query(on,dfn[x] - 1);
}
void Process(int u) {
int s = Qry[u].size();
for(int i = 0 ; i < s ; ++i) {
if(Qry[u][i].se > 0) ans[Qry[u][i].fi] -= Query_Range(0,Qry[u][i].se);
if(Qry[u][i].se < 0) ans[Qry[u][i].fi] += Query_Range(1,-Qry[u][i].se);
}
s = ver[u].size();
for(int i = 0 ; i < s ; ++i) {
if(ver[u][i] > 0) insert(0,dfn[ver[u][i]]);
if(ver[u][i] < 0) insert(1,dfn[-ver[u][i]]);
}
s = son[u].size();
for(int i = 0 ; i < s ; ++i) {
Process(son[u][i]);
}
s = Qry[u].size();
for(int i = 0 ; i < s ; ++i) {
if(Qry[u][i].se > 0) ans[Qry[u][i].fi] += Query_Range(0,Qry[u][i].se);
if(Qry[u][i].se < 0) ans[Qry[u][i].fi] -= Query_Range(1,-Qry[u][i].se);
}
}
void Solve() {
read(K);
scanf("%s",s + 1);
H = strlen(s + 1);
read(N);
ACAM::Init();
for(int i = 1 ; i <= N ; ++i) {
scanf("%s",t + 1);
int len = strlen(t + 1);
int p = 1;
if(len > K) {
for(int j = 1 ; j <= len ; ++j) {
p = ins(p,t[j] - 33);
L[j] = p;
}
p = 1;
for(int j = len ; j >= 1 ; --j) {
p = ins(p,t[j] - 33);
R[j] = p;
}
for(int j = 0 ; j <= len - K; ++j) {
int a = j == 0 ? 1 : L[j];
int b = j + K + 1 == len + 1 ? 1 : R[j + K + 1];
Qry[a].pb(mp(i,b));
}
for(int j = 1 ; j <= len - K; ++j) {
int a = L[j],b = R[j + K];
Qry[a].pb(mp(i,-b));
}
}
else ans[i] = H - len + 1;
}
ACAM::build_ACAM();
int p = 1;
pos[0][0] = 1;
for(int i = 1 ; i <= H ; ++i) {
while(1) {
if(ACAM::tr[p][s[i] - 33]) {p = ACAM::tr[p][s[i] - 33];break;}
else if(p == 1) break;
p = ACAM::fail[p];
}
pos[0][i] = p;
}
p = 1;
pos[1][H + 1] = 1;
for(int i = H ; i >= 1 ; --i) {
while(1) {
if(ACAM::tr[p][s[i] - 33]) {p = ACAM::tr[p][s[i] - 33];break;}
else if(p == 1) break;
p = ACAM::fail[p];
}
pos[1][i] = p;
}
dfs(1);
for(int i = 0 ; i <= H - K; ++i) {
ver[pos[0][i]].pb(pos[1][i + K + 1]);
}
for(int i = 1 ; i <= H - K; ++i) {
ver[pos[0][i]].pb(-pos[1][i + K]);
}
Process(1);
for(int i = 1 ; i <= N ; ++i) {
out(ans[i]);enter;
}
}
int main() {
#ifdef ivorysi
freopen("f1.in","r",stdin);
#endif
Solve();
}

【LOJ】#2278. 「HAOI2017」字符串的更多相关文章

  1. loj#2312. 「HAOI2017」八纵八横(线性基 线段树分治)

    题意 题目链接 Sol 线性基+线段树分治板子题.. 调起来有点自闭.. #include<bits/stdc++.h> #define fi first #define se secon ...

  2. 【LOJ】#3095. 「SNOI2019」字符串

    LOJ#3095. 「SNOI2019」字符串 如果两个串\(i,j\)比较\(i < j\),如果离\(a_{i}\)最近的不同的数是\(a_{k}\),如果\(j < k\)那么\(i ...

  3. LOJ #6436. 「PKUSC2018」神仙的游戏(字符串+NTT)

    题面 LOJ #6436. 「PKUSC2018」神仙的游戏 题解 参考 yyb 的口中的长郡最强选手 租酥雨大佬的博客 ... 一开始以为 通配符匹配 就是类似于 BZOJ 4259: 残缺的字符串 ...

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

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

  5. Loj #3057. 「HNOI2019」校园旅行

    Loj #3057. 「HNOI2019」校园旅行 某学校的每个建筑都有一个独特的编号.一天你在校园里无聊,决定在校园内随意地漫步. 你已经在校园里呆过一段时间,对校园内每个建筑的编号非常熟悉,于是你 ...

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

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

  7. Loj #3096. 「SNOI2019」数论

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

  8. Loj #3093. 「BJOI2019」光线

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

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

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

随机推荐

  1. 【刷题】BZOJ 2151 种树

    Description A城市有一个巨大的圆形广场,为了绿化环境和净化空气,市政府决定沿圆形广场外圈种一圈树.园林部门得到指令后,初步规划出n个种树的位置,顺时针编号1到n.并且每个位置都有一个美观度 ...

  2. 【OpenCV】摄像机标定+畸变校正

      摄像机标定 本文目的在于记录如何使用MATLAB做摄像机标定,并通过OpenCV进行校正后的显示. 首先关于校正的基本知识通过OpenCV官网的介绍即可简单了解: http://docs.open ...

  3. 使用Pypi镜像源加速第三方库的安装

    Pypi是个好东西,类似于yum和apt-get,安装第三方库相当的方便,头疼的一点就是Python的官方源服务器在国内访问的速度确实不咋滴,好在douban开放了一个国内的镜像源,速度杠杠的. 如果 ...

  4. 【转】结构struct 联合Union和枚举Enum的细节讨论

    结构struct 联合Union和枚举Enum的细节讨论 联合(Union)是一种构造数据类型,它提供了一种使不同类型数据类型成员之间共享存储空间的方法,同时可以实现不同类型数据成员之间的自动类型转换 ...

  5. 洛谷 P2303 [SDOi2012]Longge的问题 解题报告

    P2303 [SDOi2012]Longge的问题 题目背景 SDOi2012 题目描述 Longge的数学成绩非常好,并且他非常乐于挑战高难度的数学问题.现在问题来了:给定一个整数\(N\),你需要 ...

  6. 怎么在sublime里面显示编码格式

    我要在sublime text里面显示编码格式 点击Preference—settings 然后再user里面加入这个 // Display file encoding in the status b ...

  7. Spark记录-Scala函数与闭包

    函数声明 Scala函数声明具有以下形式 - def functionName ([list of parameters]) : [return type] Scala 如果不使用等号和方法体,则隐式 ...

  8. R9—R常用函数分类汇总

    数据结构 一.数据管理 vector:向量 numeric:数值型向量 logical:逻辑型向量 character:字符型向量 list:列表 data.frame:数据框 c:连接为向量或列表 ...

  9. Jerasure库简介及使用范例

    刚刚写这篇文章之前看了下上一篇博客的时间:2013年7月19日.居然已经过了3个月了!好快!感叹时间的同时不由的又感叹了下自己的懒惰,其实仔细想想,这段时间自己也做了很多事情: 完成了一篇副本同步相关 ...

  10. 2016-2017-2 20155309 南皓芯java第六周学习总结

    教材内容详解 这一次主要学习的是第十章与第十一章的内容.主要讲述了串流,字符处理和线程以及并行API. 输入输出 串流:Java中的数据有来源(source)和目的地(destination),衔接两 ...