https://www.hackerrank.com/challenges/how-many-substrings/problem

题解

似乎是被毒瘤澜澜放弃做T3的一道题(因为ASDFZ有很多人做过,当然,他换了一道更毒瘤的……)

仓鼠在最后一天的时候提了一嘴然后我发现依旧菜菜的不会……(因为太菜模拟天天被吊打)

仓鼠好神仙啊%%%(烤熟了味道怎么样啊

这道题三个数据结构结合在一起使用,但是感觉却没有那么码农,就是直接把板子套一套(当然板子不熟另当别论),用namespace的话可以降低编码复杂度(给起名废留了一条活路)

我们考虑离线

也就是对于一个右端点r,维护左端点在\([1,r]\)的答案

我们直接建一个后缀树出来(是反串的后缀树……就是直接正着建后缀自动机,把父亲节点连起来就好了)

那么当右端点往后扩展一格的时候,我们只需要考虑一下增量就好了

如果对于一个起点v,\([v,r]\)这个串能被统计的仅当不存在一个\(p<=r\),\([p - r + v,p]\)和\([v,r]\)相等

然后看了一眼我们画的后缀树……

如果我们能在另外一处找到某个位置p代表的节点,这个节点和r所代表的节点的lca到根的字符串长度,就是p能影响到r的范围

那么我们就考虑对r祖先的每个节点,维护一个在子树里除了r以外最大值

具体操作就是每次Access一下,这一条链的值就改变了

那么最后想一下,我们r要特殊处理的那些p,在r到根的路径上被分成一段一段的,那就是r到根上路过的所有偏爱链,我们Access的时候都会考虑到,把它们记录下来然后更新线段树就好

我们具体操作的时候可以让线段树里每个节点x代表从x开始的本质不同的字符串有多少

每次在前面减掉在后面出现过的串就好

查询的时候就查询一段区间和

你说我写的你看不懂,那你看看代码吧,如果还看不懂就画画后缀树就知道了

代码

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#define fi first
#define se second
#define pii pair<int,int>
//#define ivorysi
#define mp make_pair
#define pb push_back
#define enter putchar('\n')
#define space putchar(' ')
#define MAXN 100005
using namespace std;
typedef long long int64;
typedef double db;
template<class T>
void read(T &res) {
res = 0;T f = 1;char c = getchar();
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 N,Q,cnt,ver[MAXN];
pii MK[MAXN];
char s[MAXN];
int64 ans[MAXN];
struct qry_node {
int l,r,id;
friend bool operator < (qry_node &a,qry_node &b) {
return a.r < b.r;
};
}qry[MAXN];
namespace sam {
struct sam_node {
sam_node *nxt[26],*par;
int len,cnt,id;
}pool[MAXN * 2],*tail = pool,*root,*last,*que[MAXN * 2];
int c[MAXN];
void build_sam(int c,int l) {
sam_node *nowp = tail++,*p;
nowp->len = l;nowp->cnt = 1;
for(p = last ; p && !p->nxt[c] ; p = p->par) {
p->nxt[c] = nowp;
}
if(!p) nowp->par = root;
else {
sam_node *q = p->nxt[c];
if(q->len == p->len + 1) nowp->par = q;
else {
sam_node *copyq = tail++;
*copyq = *q;
copyq->cnt = 0;copyq->len = p->len + 1;
q->par = nowp->par = copyq;
for( ; p && p->nxt[c] == q; p = p->par) {
p->nxt[c] = copyq;
}
}
}
last = nowp;
}
}
namespace seg_tr {
struct seg_tr_node {
int l,r;
int64 lz,sum;
}tr[MAXN * 4];
void build(int u,int l,int r) {
tr[u].l = l;tr[u].r = r;
tr[u].lz = tr[u].sum = 0;
if(l == r) return;
int mid = (l + r) >> 1;
build(u << 1,l,mid);
build(u << 1 | 1,mid + 1,r);
}
void add_lazy(int u,int64 v) {
tr[u].lz += v;tr[u].sum += v * (tr[u].r - tr[u].l + 1);
}
void push_down(int u) {
if(tr[u].lz) {
add_lazy(u << 1,tr[u].lz);
add_lazy(u << 1 | 1,tr[u].lz);
tr[u].lz = 0;
}
}
void update(int u) {
tr[u].sum = tr[u << 1].sum + tr[u << 1 | 1].sum;
}
void add(int u,int l,int r,int64 v) {
if(tr[u].l == l && tr[u].r == r) {
add_lazy(u,v);return;
}
int mid = (tr[u].l + tr[u].r) >> 1;
push_down(u);
if(r <= mid) add(u << 1,l,r,v);
else if(l > mid) add(u << 1 | 1,l,r,v);
else {add(u << 1,l,mid,v);add(u << 1 | 1,mid + 1,r,v);}
update(u);
}
int64 query(int u,int l,int r) {
if(tr[u].l == l && tr[u].r == r) return tr[u].sum;
push_down(u);
int mid = (tr[u].l + tr[u].r) >> 1;
if(r <= mid) return query(u << 1,l,r);
else if(l > mid) return query(u << 1 | 1,l,r);
else return query(u << 1,l,mid) + query(u << 1 | 1,mid + 1,r);
}
}
namespace lct {
struct node {
node *lc,*rc,*fa;
int pos,cov,len;
void cover(int v) {
cov = v;
pos = v;
}
void push_down() {
if(cov) {
if(lc) lc->cover(cov);
if(rc) rc->cover(cov);
cov = 0;
}
}
void update() {
if(lc) pos = max(lc->pos,pos);
if(rc) pos = max(rc->pos,pos);
}
}*tr[MAXN * 2],*que[MAXN * 2];
void init() {
for(int i = 0 ; i <= 2 * N ; ++i) {
tr[i] = new node;
tr[i]->lc = tr[i]->rc = tr[i]->fa = NULL;
tr[i]->pos = tr[i]->cov = 0;
}
}
bool isRoot(node *u) {
if(!u->fa) return true;
else return u->fa->lc != u && u->fa->rc != u;
}
bool which(node *u) {
return u->fa->rc == u;
}
void rotate(node *u) {
node *v = u->fa,*w = v->fa;
if(!isRoot(v) && w) {(v == w->lc ? w->lc : w->rc) = u;}
node *b = u == v->lc ? u->rc : u->lc;
u->fa = w;v->fa = u;
if(b) b->fa = v;
if(u == v->lc) {u->rc = v;v->lc = b;}
else {u->lc = v;v->rc = b;}
v->update();
}
void Splay(node *u) {
int tot = 0;
node *x;
for(x = u ; !isRoot(x) ; x = x->fa) {
que[++tot] = u;
}
que[++tot] = x;
for(int i = tot ; i >= 1 ; --i) {
que[i]->push_down();
}
while(!isRoot(u)) {
if(!isRoot(u->fa)) {
if(which(u->fa) == which(u)) rotate(u->fa);
else rotate(u);
}
rotate(u);
}
u->update();
}
void Access(node *u,int v) {
cnt = 0;
u->cover(v);
for(node *x = NULL ; u ; x = u ,u = u->fa) {
Splay(u);
MK[++cnt] = mp(u->len,u->pos);
u->rc = x;
u->update();
}
}
};
void Init() {
read(N);read(Q);
scanf("%s",s + 1);
lct::init();
seg_tr::build(1,1,N);
sam::root = sam::last = sam::tail++;
for(int i = 1 ; i <= N ; ++i) {
sam::build_sam(s[i] - 'a',i);
}
int m = sam::tail - sam::pool;
for(int i = 0 ; i < m ; ++i) {
sam::c[sam::pool[i].len]++;
}
for(int i = 1 ; i <= N ; ++i) {
sam::c[i] += sam::c[i - 1];
}
for(int i = 0 ; i < m ; ++i) {
sam::que[sam::c[sam::pool[i].len]--] = &sam::pool[i];
}
for(int i = 1 ; i <= m ; ++i) {
sam::que[i]->id = i;
if(sam::que[i]->cnt) ver[sam::que[i]->len] = i;
}
for(int i = 1 ; i <= m ; ++i) {
lct::tr[i]->len = sam::que[i]->len;
if(sam::que[i]->par) {
lct::tr[i]->fa = lct::tr[sam::que[i]->par->id];
}
}
int l,r;
for(int i = 1 ; i <= Q ; ++i) {
read(l);read(r);++l;++r;
qry[i] = (qry_node){l,r,i};
}
sort(qry + 1,qry + Q + 1);
}
void Solve() {
int p = 1;
for(int i = 1 ; i <= N ; ++i) {
seg_tr::add(1,1,i,1);
lct::Access(lct::tr[ver[i]],i);
int last = 0;
for(int j = cnt ; j > 1 ; --j) {
if(MK[j].fi == 0) continue;
if(MK[j].se != 0) {
seg_tr::add(1,MK[j].se - MK[j].fi + 1,MK[j].se - last,-1);
}
last = MK[j].fi;
}
while(p <= Q && qry[p].r == i) {
ans[qry[p].id] = seg_tr::query(1,qry[p].l,qry[p].r);
++p;
}
}
for(int i = 1 ; i <= Q ; ++i) {
out(ans[i]);enter;
}
}
int main() {
#ifdef ivorysi
freopen("f1.in","r",stdin);
#endif
Init();
Solve();
return 0;
}

【HackerRank】How Many Substrings?的更多相关文章

  1. 【LeetCode】647. Palindromic Substrings 解题报告(Python)

    [LeetCode]647. Palindromic Substrings 解题报告(Python) 标签: LeetCode 题目地址:https://leetcode.com/problems/p ...

  2. 【CodeForces】914 F. Substrings in a String bitset

    [题目]F. Substrings in a String [题意]给定小写字母字符串s,支持两种操作:1.修改某个位置的字符,2.给定字符串y,查询区间[l,r]内出现y多少次.|s|,Σ|y|&l ...

  3. 【Leetcode】647. Palindromic Substrings

    Description Given a string, your task is to count how many palindromic substrings in this string. Th ...

  4. 【HackerRank】Running Time of Quicksort

    题目链接:Running Time of Quicksort Challenge In practice, how much faster is Quicksort (in-place) than I ...

  5. 【LeetCode】467. Unique Substrings in Wraparound String

    Consider the string s to be the infinite wraparound string of "abcdefghijklmnopqrstuvwxyz" ...

  6. 【LeetCode】1180. Count Substrings with Only One Distinct Letter 解题报告(C++)

    作者: 负雪明烛 id: fuxuemingzhu 个人博客:http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题方法 组合数 日期 题目地址:https://leetcod ...

  7. 【LeetCode】467. Unique Substrings in Wraparound String 解题报告(Python)

    作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 题目地址: https://leetcode.com/problems/unique-s ...

  8. 【LeetCode】647. Palindromic Substrings 解题报告(Python & C++)

    作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题方法 方法一:暴力循环 方法二:固定起点向后找 方法三:动 ...

  9. 【SPOJ】694. Distinct Substrings

    http://www.spoj.com/problems/DISUBSTR/ 题意:求字符串不同子串的数目. #include <bits/stdc++.h> using namespac ...

随机推荐

  1. HDU 1564 简单博弈 水

    n*n棋盘,初始左上角有一个石头,每次放只能在相邻的四个位置之一,不能操作者输. 如果以初始石头编号为1作为后手,那么对于每次先手胜的情况其最后一步的四周的编号必定是奇数,且此时编号为偶数,而对于一个 ...

  2. bzoj 3309 反演

    $n=p_1^{a_1}p_2^{a_2}…p_k^{a_k},p_i$为素数,定义$f(n)=max(a_1,a_2…,a_k)$. 给定a,b<=1e7求$\sum\limits_{i=1} ...

  3. asp.net WebForm程序删除.designer.cs文件之后的故事

    1.介绍 正常情况下添加一个WebForm程序结构如下(命名为:myWebForm.aspx) 文件说明:.aspx文件:书写html代码部分,以及javascript,css等代码书写及引用 .as ...

  4. 在EF6.0中打印数据库操作日志

    using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.T ...

  5. HDU 4548 美素数 在线打表加数状数组

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4548 解题报告:一开始本想先打个素数表,然后每次输入L 跟R 的时候都进行暴力判断,但这题测试数据太多 ...

  6. HDU 2094 产生冠军 dfs加map容器

    解题报告:有一群人在打乒乓球比赛,需要在这一群人里面选出一个冠军,现在规定,若a赢了b,b又赢了c那么如果a与c没有比赛的话,就默认a赢了c,而如果c赢了a的话,则这三个人里面选不出冠军,还有就是如果 ...

  7. 远程连接工具PuTTY和MTPuTTY

    PuTTY是一个Telnet.SSH.rlogin.纯TCP以及串行接口连接软件 官网 http://www.chiark.greenend.org.uk/~sgtatham/putty/ putty ...

  8. git之合并分支(git merge)------(三)

    最近几天写小demo,总是自己拉取他人的代码,然后创建分支,在自己的分支上进行修改,然后提交到自己的分支,具体的这一步,我就不多讲了,因为在我的博客“工作中常用的Git操作”中有详细的介绍,今天主要讲 ...

  9. Linux服务-nginx+nfs实现共享存储

    任务目标:一台服务器进行更改,其他两台服务器访问均同步 现在的情况是: web1.html文件访问的结果是web1 现在我在Web1这台机器上更改web1.html,内容为change in web1 ...

  10. 2016.6.19——C++杂记

    C++杂记 补充的小知识点: 1.while(n--)和while(--n)区别: while(n--)即使不满足也执行一次循环后跳出. while(--n)不满足直接跳出循环,不执行语句. 用cou ...