题目大意

给定两个字符串A,B,求出A和B中最长公共子串的长度。

题目分析

字符串的子串可以认为是是字符串的某个后缀的前缀,而求最长公共子串相当于A和B的某两个后缀的最长相同前缀。可以考虑使用后缀数组,将A和B连接起来,中间添加一个在A和B中都未出现过的字符隔开,然后求这个新串的后缀数组以及height数组。**height数组是后缀Suffix(SA[i])和Suffix(SA[i-1])的公共前缀的最长长度。 
    容易知道,
满足题目要求的两个子串S1,S2在后缀数组中肯定排名相邻(用反证法可以证明)**。这样就可以利用height数组,遍历一遍 height数组,要求 SA[i]和SA[i-1]分别属于A和B,同时height最大。 
    求后缀数组,使用倍增算法,时间复杂度O(nlogn);求height数组,时间复杂度O(n);遍历height数组,求SA[i]、SA[i-1]属于不同串的height[i]最大值,时间复杂度O(n)。因此总的时间复杂度为 O(nlogn)

注意: 
    判断SA[i]和SA[i-1]属于不同的串,设n为第一个串的长度。通过(SA[i] - n)*(SA[i-1]-n) < 0时,由于数据过大,使用int类型会出现溢出,因此需要使用long long int类型。

实现(c++)
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
#define LETTERS 30
#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++){
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;
}
}
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;
}
} char str[MAX_ARRAY_SIZE];
int main(){
scanf("%s", str);
int n = strlen(str);
str[n] = 'a' + 27;
scanf("%s", str + n + 1);
GetStr(str);
GetSuffixArray();
GetHeight();
int max = 0;
for (int i = 1; i < gStrLen; i++){
if (gHeight[i] > max){
if ((gSuffixArray[i] > n && gSuffixArray[i-1] < n) || (gSuffixArray[i - 1] > n && gSuffixArray[i] < n)){
max = gHeight[i];
}
}
}
printf("%d\n", max);
return 0;
}

poj_2774 后缀数组的更多相关文章

  1. 后缀数组的倍增算法(Prefix Doubling)

    后缀数组的倍增算法(Prefix Doubling) 文本内容除特殊注明外,均在知识共享署名-非商业性使用-相同方式共享 3.0协议下提供,附加条款亦可能应用. 最近在自学习BWT算法(Burrows ...

  2. BZOJ 4199: [Noi2015]品酒大会 [后缀数组 带权并查集]

    4199: [Noi2015]品酒大会 UOJ:http://uoj.ac/problem/131 一年一度的“幻影阁夏日品酒大会”隆重开幕了.大会包含品尝和趣味挑战两个环节,分别向优胜者颁发“首席品 ...

  3. BZOJ 1692: [Usaco2007 Dec]队列变换 [后缀数组 贪心]

    1692: [Usaco2007 Dec]队列变换 Time Limit: 5 Sec  Memory Limit: 64 MBSubmit: 1383  Solved: 582[Submit][St ...

  4. POJ3693 Maximum repetition substring [后缀数组 ST表]

    Maximum repetition substring Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 9458   Acc ...

  5. POJ1743 Musical Theme [后缀数组]

    Musical Theme Time Limit: 1000MS   Memory Limit: 30000K Total Submissions: 27539   Accepted: 9290 De ...

  6. 后缀数组(suffix array)详解

    写在前面 在字符串处理当中,后缀树和后缀数组都是非常有力的工具. 其中后缀树大家了解得比较多,关于后缀数组则很少见于国内的资料. 其实后缀数组是后缀树的一个非常精巧的替代品,它比后缀树容易编程实现, ...

  7. 【UOJ #35】后缀排序 后缀数组模板

    http://uoj.ac/problem/35 以前做后缀数组的题直接粘模板...现在重新写一下模板 注意用来基数排序的数组一定要开到N. #include<cstdio> #inclu ...

  8. 【BZOJ-2119】股市的预测 后缀数组

    2119: 股市的预测 Time Limit: 10 Sec  Memory Limit: 259 MBSubmit: 334  Solved: 154[Submit][Status][Discuss ...

  9. 【BZOJ-4698】Sandy的卡片 后缀数组

    4698: Sdoi2008 Sandy的卡片 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 140  Solved: 55[Submit][Stat ...

随机推荐

  1. PS_图象调整_太暗/过亮_曝光不足/过度

    对于曝光不足,图像太暗. 1.调整[色阶] 图象>调整>色阶   clrl+L 然后拖动"黑","灰","白"三个滑块. 2.使 ...

  2. 【Unity/Kinect】获取预制的手势信息KinectInterop.HandState

    Kinect使用了枚举KinectInterop.HandState来描述手势. 该手势指的是手掌的状态(张开/握拳),而不是说整个手臂的肢体动作(Gesture). 同样是需要嵌套在Kinect获取 ...

  3. Nhibernate 一对一,一对多,多对多 成功映射

    前语: 在Nhibernate xml 的文件配置上,一对一和多对多的配置比较简单,容易出错的反而是一对多(多对一)上. 1.一对一关联关系的映射: <one-to-one name=" ...

  4. (转) eclipse安装lombok

    lombok的官方网址:http://projectlombok.org/ 1. lombok的安装: 使用lombox是需要安装的,如果不安装,IDE则无法解析lombox注解,有两种方式可以安装l ...

  5. Python递归实现汉诺塔

    Python递归实现汉诺塔: def f3(n,x,y,z): if(n==1): print(x,'--->',z) else: f3(n-1,x,z,y) print(x,'--->' ...

  6. C++中的类继承之单继承&多继承&菱形继承

     C++中的类继承之单继承&多继承&菱形继承 单继承是一般的单一继承,一个子类只 有一个直接父类时称这个继承关系为单继承.这种关系比较简单是一对一的关系: 多继承是指 一个子类有两个或 ...

  7. php 抽象 继承 多态

    1.继承和重载 !!!php中使用extends单一继承的方法 被继承的类  父类(基类) 继承者   子类(派生类) 如果说我们需要将父类方法重载(方法覆盖),在派生类里使用与基类方法重名的方法名称 ...

  8. C语言 字面量

    在计算机科学中,字面量(literal)是用于表达源代码中一个固定值的表示法(notation). 几乎所有计算机编程语言都具有对基本值的字面量表示,诸如:整数.浮点数以及字符串: 而有很多也对布尔类 ...

  9. Qt 事件过滤器

    Qt创建了QEvent事件对象之后,会调用QObject的event()函数做事件的分发.有时候,你可能需要在调用event()函数之前做一些另外的操作,比如,对话框上某些组件可能并不需要响应回车按下 ...

  10. 关于Cocos2d-x中实例伸缩后的位置设置

    在有的时候觉得图片太大,会进行缩放,但是在设置位置的时候,用fire->getContentSize(),用的是它原来的大小,就会产生不能准确设置节点的现象 1.在设置伸缩比例的时候,记住比例值 ...