题目描述

(我并不想告诉你题目名字是什么鬼)

有一个长度为n的仅包含小写字母的字符串S,下标范围为[1,n].

现在有若干组询问,对于每一个询问,我们给出若干个后缀(以其在S中出现的起始位置来表示),求这些后缀两两之间的LCP(LongestCommonPrefix)的长度之和.一对后缀之间的LCP长度仅统计一遍.

输入

第一行两个正整数n,m,分别表示S的长度以及询问的次数.

接下来一行有一个字符串S.

接下来有m组询问,对于每一组询问,均按照以下格式在一行内给出:

首先是一个整数t,表示共有多少个后缀.接下来t个整数分别表示t个后缀在字符串S中的出现位置.

输出

对于每一组询问,输出一行一个整数,表示该组询问的答案.由于答案可能很大,仅需要输出这个答案对于23333333333333333(一个巨大的质数)取模的余数.

样例输入

7 3
popoqqq
1 4
2 3 5
4 1 2 5 6

样例输出

0
0
2


题解

后缀数组+倍增RMQ+单调栈

首先预处理出sa和height数组。

然后对于每组询问,将要求的后缀去重后按照rank从小到大排序。

由于我们有:LCP(a,c)=min(LCP(a,b),LCP(b,c)),其中rank[a]<rank[b]<rank[c]

所以我们只需要知道相邻两个要求的后缀之间的LCP,即可推出任意两个后缀的LCP。

这里求LCP的方式是倍增RMQ,所以我偷改了height的定义:height[i][j]表示排名为i-2^j的后缀与排名为i的后缀的LCP。

这样转化成了一个新的问题:给你n个数,求其每个子区间中最小值的和。

考虑对答案的贡献:ai对答案的贡献是满足l∈[lpos,i],r∈[i,rpos]的所有区间[l,r],也即ai*(i-lpos+1)*(rpos-i+1),其中lpos是i左侧最后一个大于i的,rpos是i右侧最后一个大于等于i的。

(左右包含等号的情况不同是为了处理相同的数,防止重复或漏算)

可以用一个单调栈来在线性时间内求出i-lpos+1和rpos-i+1,具体方法见代码。

最后的最后,需要把用于去重的数组vis清零,注意不能用memset。

#include <cstdio>
#include <cstring>
#include <algorithm>
#define N 500010
#define mod 23333333333333333ll
using namespace std;
int n , m , sa[N] , r[N] , ws[N] , wa[N] , wb[N] , wv[N] , rank[N] , height[N][21] , log[N] , num[N * 6] , vis[N] , pos[N] , val[N] , sta[N] , top , lp[N] , rp[N];
char str[N];
void da()
{int i , j , p , *x = wa , *y = wb;for(i = 0 ; i < m ; i ++ ) ws[i] = 0;for(i = 0 ; i < n ; i ++ ) ws[x[i] = r[i]] ++ ;for(i = 1 ; i < m ; i ++ ) ws[i] += ws[i - 1];for(i = n - 1 ; i >= 0 ; i -- ) sa[--ws[x[i]]] = i;for(p = j = 1 ; p < n ; j <<= 1 , m = p){for(p = 0 , i = n - j ; i < n ; i ++ ) y[p ++ ] = i;for(i = 0 ; i < n ; i ++ ) if(sa[i] - j >= 0) y[p ++ ] = sa[i] - j;for(i = 0 ; i < n ; i ++ ) wv[i] = x[y[i]];for(i = 0 ; i < m ; i ++ ) ws[i] = 0;for(i = 0 ; i < n ; i ++ ) ws[wv[i]] ++ ;for(i = 1 ; i < m ; i ++ ) ws[i] += ws[i - 1];for(i = n - 1 ; i >= 0 ; i -- ) sa[--ws[wv[i]]] = y[i];for(swap(x , y) , x[sa[0]] = 0 , p = i = 1 ; i < n ; i ++ ){if(y[sa[i - 1]] == y[sa[i]] && y[sa[i - 1] + j] == y[sa[i] + j]) x[sa[i]] = p - 1;else x[sa[i]] = p ++ ;}}for(i = 1 ; i < n ; i ++ ) rank[sa[i]] = i;for(p = i = 0 ; i < n - 1 ; height[rank[i ++ ]][0] = p)for(p ? p -- : 0 , j = sa[rank[i] - 1] ; r[i + p] == r[j + p] ; p ++ );
}
int query(int x , int y)
{x ++ ;int k = log[y - x + 1];return min(height[x + (1 << k) - 1][k] , height[y][k]);
}
bool cmp(int a , int b)
{return rank[a] < rank[b];
}
int main()
{int i , j , k , cnt , tot;long long ans;scanf("%d%d%s" , &n , &k , str);for(i = 0 ; i < n ; i ++ ) r[i] = str[i] - 'a' + 1;n ++ , m = 28 , da() , n -- ;for(i = 2 ; i <= n ; i ++ ) log[i] = log[i >> 1] + 1;for(i = 1 ; i <= log[n] ; i ++ )for(j = (1 << i) ; j <= n ; j ++ )height[j][i] = min(height[j][i - 1] , height[j - (1 << (i - 1))][i - 1]);while(k -- ){scanf("%d" , &cnt);tot = 0 , ans = 0;for(i = 1 ; i <= cnt ; i ++ ){scanf("%d" , &num[i]) , num[i] -- ;if(!vis[num[i]]) vis[num[i]] = 1 , pos[++tot] = num[i];}sort(pos + 1 , pos + tot + 1 , cmp);for(i = 1 ; i < tot ; i ++ )val[i] = query(rank[pos[i]] , rank[pos[i + 1]]);sta[0] = top = 0;for(i = 1 ; i < tot ; i ++ ){while(top && val[sta[top]] > val[i]) top -- ;lp[i] = i - sta[top] , sta[++top] = i;}sta[0] = tot , top = 0;for(i = tot - 1 ; i  ; i -- ){while(top && val[sta[top]] >= val[i]) top -- ;rp[i] = sta[top] - i , sta[++top] = i;}for(i = 1 ; i < tot ; i ++ ) ans = (ans + (long long)lp[i] * rp[i] * val[i]) % mod;printf("%lld\n" , ans);for(i = 1 ; i <= cnt ; i ++ ) vis[num[i]] = 0;}return 0;
}

转载于:https://www.cnblogs.com/GXZlegend/p/6871613.html

【bzoj3879】SvT 后缀数组+倍增RMQ+单调栈相关推荐

  1. [bzoj3879]SvT_后缀数组_RMQ_单调栈

    SvT bzoj-3879 题目大意:给定一个字符串.每次询问给定$t$个位置,求两两位置开头的后缀的$LCP$之和. 注释:$1\le length\le 5\cdot 10^5$,$\sum t\ ...

  2. 后缀数组 倍增法详解

    算法思路 主要是基于基数排序,如果基数排序没弄懂代码就会很难理解: 首先从k=0开始,从后缀数组里面选取步长为2^k的后缀数组的前子串 然后进行基数排序 如果排序后所有的名次数组的值都不相同,那么排序 ...

  3. UOJ.35.[模板]后缀排序(后缀数组 倍增)

    题目链接 论找到一个好的教程的正确性.. 后缀数组 下标从1编号: //299ms 2560kb #include <cstdio> #include <cstring> #i ...

  4. codefores 204E. Little Elephant and Strings(后缀数组,RMQ求lcp,二分,主席树)

    题目链接 E. Little Elephant and Strings time limit per test3 seconds memory limit per test256 megabytes ...

  5. BZOJ 2780: [Spoj]8093 Sevenk Love Oimaster( 后缀数组 + 二分 + RMQ + 树状数组 )

    全部串起来做SA, 在按字典序排序的后缀中, 包含每个询问串必定是1段连续的区间, 对每个询问串s二分+RMQ求出包含s的区间. 然后就是求区间的不同的数的个数(经典问题), sort queries ...

  6. 【BZOJ 2119】 2119: 股市的预测 (后缀数组+分块+RMQ)

    2119: 股市的预测 Time Limit: 10 Sec  Memory Limit: 259 MB Submit: 404  Solved: 188 Description 墨墨的妈妈热爱炒股, ...

  7. POJ.2774.Long Long Message/SPOJ.1811.LCS(后缀数组 倍增)

    题目链接 POJ2774 SPOJ1811 LCS - Longest Common Substring 比后缀自动机慢好多(废话→_→). \(Description\) 求两个字符串最长公共子串 ...

  8. 备战NOI 数据结构——栈与单调栈(stack) 以及后缀表达式

    栈 stack 引入 栈的概念 代码实现 定义和初始化(init) 入栈(push) 出栈(pop) 访问栈顶元素(query) 查询栈的元素个数(size) 判断是否为空(empty) 清空栈(cl ...

  9. [学习笔记]后缀数组

    参考: 后缀数组 最详细讲解 上面一篇是转载这一篇的: 后缀数组 学习笔记 一. 后缀:suff(i),后缀要排序 sa[i],排名为i的后缀开始位置 rk[i],i开始位置的后缀的排名. rk[sa ...

最新文章

  1. python编写测试工具-python 写一个性能测试工具(一)
  2. ExtJS + Gears
  3. linux wc read,Linux 下使用 wc 统计文件夹下所有文件的代码行数(包括子目录)-Go语言中文社区...
  4. cdrx4自动排版步骤_Coreldraw插件emboss使用方法 CDRX4的自动排版插件 百分之百显示问题...
  5. Markdown中表格中内容换行、左对齐等基本操作
  6. Overture五线谱打曲谱用得上的排版技巧
  7. Blend Shape Pose Animation
  8. 20佳用于手绘风格网页设计的字体素材 !
  9. androidP 对反射的限制之黑名单机制
  10. Bootstrap 网格系统
  11. 安全多方计算之隐私保护集合交集
  12. java计算机毕业设计某山区环境保护监督管理平台源程序+mysql+系统+lw文档+远程调试
  13. originos系统和鸿蒙系统哪个好用,originos和emui11哪个好用 originos和emui11使用对比...
  14. 精选20个爆火的Python实战项目(含源码),直接拿走不谢
  15. Java之BigInteger的用法
  16. 基于雷电模拟器的python脚本(乱斗西游2自动收礼包)
  17. java double 后缀_Java double数据类型
  18. windows查看wifi密码
  19. LPWSTR类型字符串的用法
  20. 5,000名智利商家现接受加密货币支付

热门文章

  1. 技术人的生命之源在于绝不固步自封而不断进取的精神
  2. 解答网友shell问题一例20140702
  3. Tar打包、压缩与解压缩到指定目录的方法
  4. Java的日期API真烂
  5. DataGrid内容的导出
  6. colab如何通过<>来直接加入相对应的代码段呢?模块化代码操作,真好
  7. 加州圣地亚哥电子和计算机工程研究生申请入口
  8. ae的渲染引擎:cineware
  9. 《守望先锋》阵亡镜头、全场最佳和亮眼表现是如何设计
  10. 安卓上为什么不能用system.io.file读取streammingAssets目录下的文件