poj_3415 后缀数组+单调栈
题目大意
定义字符串T的子串T(i, k)=TiTi+1...Ti+k-1, 1≤i≤i+k-1≤|T|. 给定两个字符串A和B,定义集合S为S = {(i, j, k) | k≥K, A(i, k)=B(j, k)}.
对于给定的字符串A和B,求出满足要求的集合S的大小。
题目分析
就是求A和B中有多少个不同的起始位置A(i),B(j)和长度k的组合,使得子串A(i),A(i+1)....A(i+k-1)和子串B(j),B(j+1)....B(i+k-1)相同。
艾玛。。这种奇葩的字符串问题,考虑使用后缀数组,尤其是后缀数组的height数组。先将A和B连接起来,中间用一个不在A和B中出现的字符c隔开,然后求后缀数组,再用height[i] >= k 这个限制条件将排好序的后缀分成若干组。然后在每组内进行考虑:
(1)组内的元素两两之间的公共前缀长度都>=k,且A的子串和B的子串混乱排列的。按照后缀的排序顺序,依次考虑A的子串Sai和Sai之前的B的子串Sbj,Sbj+1...Sbk能够构成的(i,j,k)三元组的个数。对于某个Sbj来说,Sai和Sbj能够构成的三元组个数为 Sai和Sbj的公共前缀长度 LCP - k + 1,而Sai和Sbj的公共前缀的长度LCP为Sai和Sbj中间的height数组的最小值(Sai到Sbj排好序的后缀)。
(2)从上到下(已经将后缀排好序)依次考虑每个A子串和它之前的B子串可构成的三元组的个数,再从上到下依次考虑每个B子串和它之前的A子串可构成的三元组的个数。下面以计算A子串之前的B子串与之构成的三元组的个数为例。
(3)如上图所示,该组内的后缀有{A1, B1, B2, A2, A3, B3, A4},他们两两的公共前缀长度均大于等于k。 其中A1和B1的公共前缀长度为H1, B1和B2公共前缀长度为H2.... (图中没有画出字符串后缀,字符串后缀为蓝色Height柱两边的空白,蓝色Height柱代表相邻后缀串的公共前缀)。从左向右分析每个height,用一个变量tot记录当前点之前的所有B的子串可能和当前点及其之后的某个A子串构成的三元组的个数。用一个单调栈进行维护数据,栈中的元素为(后缀的序号和栈中栈顶下一个元素表示的后缀之间的B子串的个数count,后缀和之前后缀的最长公共前缀长度height[i])。若当前的height[i] 大于栈顶的元素的gStacktop,则入栈,否则不断弹栈,直到栈顶元素 gStacktop < height[i],再入栈。
当某个元素出栈的时候,更新tot的值。
艾玛。。。。累死了,不是自己的思路。。感觉对这里单调栈的应用还是不很理解,只是知道这么做是对的,以及为什么这么做。但是不知道这种做法是如何想出来的,所以无法用自己的语言描述很清楚。
这里单调栈的思路是直接参考大牛的思路:poj_3415
实现(c++)
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
#include<vector>
using namespace std;
#define LETTERS 60
#define MAX_ARRAY_SIZE 200005 int gSuffixArray[MAX_ARRAY_SIZE];
int gCount[MAX_ARRAY_SIZE];
int gOrderBySecondKey[MAX_ARRAY_SIZE];
int gRank[MAX_ARRAY_SIZE];
int gFirstKeyArray[MAX_ARRAY_SIZE];
int gHeight[MAX_ARRAY_SIZE]; int gStr[MAX_ARRAY_SIZE];
int gStrLen; bool Compare(int* arr, int a, int b, int step){
return arr[a] == arr[b] && arr[a + step] == arr[b + step];
} void GetStr(char* str){
memset(gStr, 0, sizeof(gStr));
gStrLen = strlen(str);
for (int i = 0; i < gStrLen; i++){
if (str[i] >= 'a'){
gStr[i] = str[i] - 'a' + 27;
}
else{
gStr[i] = str[i] - 'A' + 1;
}
}
gStr[gStrLen] = 0;
gStrLen++;
}
//求后缀数组
void GetSuffixArray(){
int n = gStrLen;
memset(gCount, 0, sizeof(gCount));
for (int i = 0; i < n; i++){
gRank[i] = gStr[i];
gCount[gRank[i]] ++;
}
int m = LETTERS;
for (int i = 1; i < m; i++){
gCount[i] += gCount[i - 1];
}
for (int i = n - 1; i >= 0; i--){
gSuffixArray[--gCount[gRank[i]]] = i;
} int step = 1;
int *rank = gRank, *order_by_second_key = gOrderBySecondKey;
while (step < n){
int p = 0; for (int i = n - step; i < n; i++){
order_by_second_key[p++] = i;
}
for (int i = 0; i < n; i++){
if (gSuffixArray[i] >= step){
order_by_second_key[p++] = gSuffixArray[i] - step;
}
}
for (int i = 0; i < n; i++){
gFirstKeyArray[i] = rank[order_by_second_key[i]];
}
for (int i = 0; i < m; i++){
gCount[i] = 0;
}
for (int i = 0; i < n; i++){
gCount[gFirstKeyArray[i]] ++;
}
for (int i = 1; i < m; i++){
gCount[i] += gCount[i - 1];
}
for (int i = n - 1; i >= 0; i--){
gSuffixArray[--gCount[gFirstKeyArray[i]]] = order_by_second_key[i];
}
int* tmp = rank; rank = order_by_second_key; order_by_second_key = tmp;
rank[gSuffixArray[0]] = p = 0;
for (int i = 1; i < n; i++){
if (Compare(order_by_second_key, gSuffixArray[i], gSuffixArray[i - 1], step)){
rank[gSuffixArray[i]] = p;
}
else{
rank[gSuffixArray[i]] = ++p;
}
}
m = p + 1;
step *= 2;
}
}
//求height数组
void GetHeight(){
int n = gStrLen;
for (int i = 0; i < n; i++){
gRank[gSuffixArray[i]] = i;
}
int k = 0, j;
for (int i = 0; i < n; i++){
if (k){
k--;
}
j = gSuffixArray[gRank[i] - 1];
while (j + k < n && i + k < n&& gStr[i + k] == gStr[j + k]){
k++;
}
gHeight[gRank[i]] = k;
}
}
int min(int a, int b){
return a < b ? a : b;
}
int gStack[MAX_ARRAY_SIZE][2];
long long int Find(int k, int n){
int end = 1;
long long int sum = 0, tot = 0;
int top = -1, count = 0;
while (end < gStrLen){
if (gHeight[end] < k){
count = 0;
tot = 0;
top = -1;
}
else{
count = 0;
if (gSuffixArray[end - 1] < n){
tot += (gHeight[end] - k + 1);
count++;
}
while (top >= 0 && gStack[top][0] >= gHeight[end]){
tot -= gStack[top][1] * (gStack[top][0] - gHeight[end]);
count += gStack[top][1];
top--;
}
top++;
gStack[top][0] = gHeight[end];
gStack[top][1] = count;
if (gSuffixArray[end] > n){
sum += tot;
}
}
end++;
}
end = 1;
tot = 0;
count = 0;
top = -1;
while (end < gStrLen){
if (gHeight[end] < k){
tot = 0;
count = 0;
top = -1;
}
else{
count = 0;
if (gSuffixArray[end - 1] > n){
count++;
tot += (gHeight[end] - k + 1);
}
while (top >= 0 && gStack[top][0] >= gHeight[end]){
tot -= (gStack[top][1])*(gStack[top][0] - gHeight[end]);
count += gStack[top][1];
top--;
}
top++; gStack[top][0] = gHeight[end];
gStack[top][1] = count; if (gSuffixArray[end] < n){
sum += tot;
}
}
end ++;
}
return sum;
}
char str[MAX_ARRAY_SIZE];
int main(){
int len1, len2, k;
while (scanf("%d", &k) != EOF){
if (k == 0){
break;
}
scanf("%s", str);
len1 = strlen(str);
str[len1] = 'a' + 27;
scanf("%s", str + len1 + 1);
GetStr(str);
GetSuffixArray();
GetHeight(); long long int sum = Find(k, len1); printf("%lld\n", sum);
}
return 0;
}
poj_3415 后缀数组+单调栈的更多相关文章
- 【BZOJ-3238】差异 后缀数组 + 单调栈
3238: [Ahoi2013]差异 Time Limit: 20 Sec Memory Limit: 512 MBSubmit: 1561 Solved: 734[Submit][Status] ...
- BZOJ_3879_SvT_后缀数组+单调栈
BZOJ_3879_SvT_后缀数组+单调栈 Description (我并不想告诉你题目名字是什么鬼) 有一个长度为n的仅包含小写字母的字符串S,下标范围为[1,n]. 现在有若干组询问,对于每一个 ...
- BZOJ_3238_[Ahoi2013]差异_后缀数组+单调栈
BZOJ_3238_[Ahoi2013]差异_后缀数组+单调栈 Description Input 一行,一个字符串S Output 一行,一个整数,表示所求值 Sample Input cacao ...
- BZOJ.4199.[NOI2015]品酒大会(后缀数组 单调栈)
BZOJ 洛谷 后缀自动机做法. 洛谷上SAM比SA慢...BZOJ SAM却能快近一倍... 显然只需要考虑极长的相同子串的贡献,然后求后缀和/后缀\(\max\)就可以了. 对于相同子串,我们能想 ...
- 【BZOJ3879】SvT 后缀数组+单调栈
[BZOJ3879]SvT Description (我并不想告诉你题目名字是什么鬼) 有一个长度为n的仅包含小写字母的字符串S,下标范围为[1,n]. 现在有若干组询问,对于每一个询问,我们给出若干 ...
- BZOJ3238 [Ahoi2013]差异 【后缀数组 + 单调栈】
题目链接 BZOJ3238 题解 简单题 经典后缀数组 + 单调栈套路,求所有后缀\(lcp\) #include<iostream> #include<cstdio> #in ...
- BZOJ4199 [Noi2015]品酒大会 【后缀数组 + 单调栈 + ST表】
题目 一年一度的"幻影阁夏日品酒大会"隆重开幕了.大会包含品尝和趣味挑战两个环节,分别向优胜者颁发"首席品 酒家"和"首席猎手"两个奖项,吸 ...
- poj 3415 Common Substrings 后缀数组+单调栈
题目链接 题意:求解两个字符串长度 大于等于k的所有相同子串对有多少个,子串可以相同,只要位置不同即可:两个字符串的长度不超过1e5; 如 s1 = "xx" 和 s2 = &qu ...
- poj 3415 Common Substrings(后缀数组+单调栈)
http://poj.org/problem?id=3415 Common Substrings Time Limit: 5000MS Memory Limit: 65536K Total Sub ...
随机推荐
- JVM 参数详解
1.使用$JAVA_HOME/bin/java 可看到所有参数说明 用法: java [-options] class [args...] (执行类) 或 java [-options] -jar ...
- Rust 1.7.0 处理命令行參数
std是 Rust 标准函数库: env 模块提供了处理环境函数. 在使用标准函数库的时候,使用 use 导入对应的 module . 一.直接输出 use std::env; fn main(){ ...
- Git Manual / Git使用手册 / Git, GitLab, Git Bash, TortoiseGit (建议全文复制到Word文档中通过导航窗格查看)
Git使用手册 目录 1 引言 2 Git.GitLab简介 2.1 Git 2.2 GitLab 2.3 Git基本概念 3 运行环境 4 ...
- HDU 1863 畅通工程 克鲁斯卡尔算法
畅通工程 Time Limit: 1000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Submis ...
- 流式布局的使用方法--Masonry
http://www.jq22.com/demo/masonry/ 范例 css部分 body { background-color: #c7cad0; } .post_box { backgroun ...
- FreeRTOS基础知识
前面一篇文章介绍了一些命名规范之类的基础知识,但是我觉得还缺少一定前言知识,就是裸机和操作系统有什么区别,为什么我们需要学freertos,因为招聘要求?那么为什么招聘网又会有这个要求呢?所以我们为什 ...
- js去掉双引号
替换: d = d.replace("\"","").replace("\"",""); 全部替换: ...
- SpringMVC之学习(1)
先来一个springmvc的基本配置,照样输出hello world 1.先导入springmvc所需要的开发包 最起码需要这么多的基本包,然后开始上配置 2.先在web.xml里进行引入,和Stru ...
- Centos 挂载NTFS格式的USB硬盘
公司的一台服务器本身硬盘容量较小,要加一块2T的硬盘,用来做samba服务器,以下是挂载硬盘的主要记录: 这是服务器不认NTFS格式的情况:mount: unknown filesystem type ...
- netifd
Netifd是OpenWrt中用于进行网络配置的守护进程,基本上所有网络接口设置以及内核的netlink事件都可以由netifd来处理完成. 在启动netifd之前用户需要将所需的配置写入uci配置文 ...