zoukankan      html  css  js  c++  java
  • 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
    ", sum);
    	}
    	return 0;
    }
    
  • 相关阅读:
    使用scp进行远程数据传输时避免输入密码(scp without password)
    scons用户指南翻译(附gcc/g++参数详解)
    StringIO 模块用于在内存缓冲区中读写数据
    注意变换的顺序
    为什么static成员必须在类外初始化
    Visual Studio中删除所有空行
    rendering order of skybox
    解决VS中注释乱码的问题
    一道数论题目
    First Chance Exception
  • 原文地址:https://www.cnblogs.com/gtarcoder/p/4836079.html
Copyright © 2011-2022 走看看