您的位置:首页 > 其它

后缀数组学习笔记

2015-01-25 13:50 309 查看
后缀数组的用处:快速求出两个后缀Suffix(i), Suffix(j)的最长公共前缀(LCP, Longest Common Perfix)

后缀数组的应用

先提出后缀数组的几种常用技巧:

建议多找找与height数组的关联。

将几个字符串贴在一起,用特殊符号间隔开:如aab与aaab,可合并成aab$aaab。

二分+分组(思想)的方法:枚举出答案后,就能将合法的情况划分到一个组判断。

以下列举出几个后缀数组的应用供大家思考。

求两个后缀的最长公共前缀

求字符串的可重叠的最长重复子串:如ababa可重叠的最长重复子串是aba

求字符串的不可重叠的最长重复子串:如ababa不可重叠的最长重复子串是ab

计算不相同子串的个数:如aaaa的不相同子串数是4

计算最长回文子串:如aabaaaab的最长回文子串是6(baaaab)。

求两个字符串的最长公共子串:如aaba与abac的最长公共子串是aba。

以下一张图片可谓简洁明了。



下面贴上模板

1.求最长重复子串,可以重叠

void solve_duplicate_substr(int n){//duplicate available


2.求最长重复子串,不可重叠

void slove_update_duplicate_substr(int n){//duplicate unavailable


  

Source Code:

 //#pragma comment(linker, "/STACK:16777216") //for c++ Compiler
#include <stdio.h>
#include <iostream>
#include <fstream>
#include <cstring>
#include <cmath>
#include <stack>
#include <string>
#include <map>
#include <set>
#include <list>
#include <queue>
#include <vector>
#include <algorithm>
#define Max(a,b) (((a) > (b)) ? (a) : (b))
#define Min(a,b) (((a) < (b)) ? (a) : (b))
#define Abs(x) (((x) > 0) ? (x) : (-(x)))
#define MOD 1000000007
#define pi acos(-1.0)

using namespace std;

typedef long long ll ;
typedef unsigned long long ull ;
typedef unsigned int uint ;
typedef unsigned char uchar ;

template<class T> inline void checkmin(T &a,T b){if(a>b) a=b;}
template<class T> inline void checkmax(T &a,T b){if(a<b) a=b;}

const double eps = 1e-7 ;
const int N = 1 ;
const int M = 200000 ;
const ll P = 10000000097ll ;
const int INF = 0x3f3f3f3f ;

int a[M], sa[M], h[M], rank[M];

void radix(int *str, int *a, int *b, int n, int m){
static int count[M];
int i;
memset(count, 0, sizeof(count));
for(i = 0; i < n; ++i) ++count[str[a[i]]];
for(i = 1; i <= m; ++i) count[i] += count[i - 1];
for(i = n - 1; i >= 0; --i) b[--count[str[a[i]]]] = a[i];
}

void suffix_array(int *str, int *sa, int n, int m){
static int a[M], b[M];
int i, j;
for(i = 0; i < n; ++i) rank[i] = i;
radix(str, rank, sa, n, m);

rank[sa[0]] = 0;
for(i = 1; i < n; ++i) rank[sa[i]] = rank[sa[i - 1]] + (str[sa[i]] != str[sa[i - 1]]);
for(i = 0; 1<<i < n; ++i){
for(j = 0; j < n; ++j){
a[j] = rank[j] + 1;
b[j] = j + (1 << i) >= n ? 0 : rank[j + (1 << i)] + 1;
sa[j] = j;
}
radix(b, sa, rank, n, n);
radix(a, rank, sa, n, n);
rank[sa[0]] = 0;
for(j = 1; j < n; ++j){
rank[sa[j]] = rank[sa[j - 1]] + (a[sa[j - 1]] != a[sa[j]] || b[sa[j - 1]] != b[sa[j]]);
}
}
}

void calc_height(int *str, int *sa, int *h, int n){
int i, k = 0;
h[0] = 0;
for(i = 0; i < n; ++i){
k = k == 0 ? 0 : k - 1;
if(rank[i] != 0){
while(str[i + k] == str[sa[rank[i] - 1] + k]){
++k;
}
}
h[rank[i]] = k;
}
}

void solve_duplicate_substr(int n){//duplicate available
int i, j, pos, ans = 0;
for(i = 0; i < n; ++i){
if(h[rank[i]] > ans){
ans = h[rank[i]];
pos = i;
}
}
for(i = pos; i < pos + ans; ++i){
printf("%c", a[i]);
}
printf("\n");
}

void slove_update_duplicate_substr(int n){//duplicate unavailableint i, j;
int low = 1, high = n;
int ans = 0, pos1 = 0, pos2 = 0;
while(low <= high){
int mid = (low + high) / 2;
bool flag = false;
for(i = 0; i < n; ){
j = i + 1;
int minPos = sa[i], maxPos = sa[i];
while(j < n && h[j] >= mid){
minPos = min(minPos, sa[j]);
maxPos = max(maxPos, sa[j]);
++j;
}
if(maxPos - minPos >= mid){
flag = true;
if(mid > ans){
ans = mid;
pos1 = minPos;
pos2 = maxPos;
}
break;
}
i = j;
}
if(flag) low = mid + 1;
else high = mid - 1;
}
for(i = pos1; i < pos1 + ans; ++i){
printf("%c", a[i]);
}
printf("\n");
}

int main(){
int i, j, t, n, m, k;
string str;
while(cin >> str){
copy(str.begin(), str.end(), a);
n = str.length();
suffix_array(a, sa, n, 256);
calc_height(a, sa, h, n);
solve_duplicate_substr(n);
slove_update_duplicate_substr(n);
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: