水题
POJ2774
求两个串的最长公共子串(最长公共前缀)。
求两个数的最长公共子串,可以将两个字符串中间加一个‘$’然后拼接成一个子串。
这样两个最长公共子串问题转换为 一个大串的最长公共前缀,因为height数组存的是 i-1 和 i位置的最长公共前缀,因此只要保证 i-1 和 i 位置的子串是不同的字符串即可。
#include
#include
#include
#include
#include
#include
const int maxn = 1000005;
using namespace std;
int t1[maxn],t2[maxn],c[maxn];
//求sa数组需要的中间变量,不需要赋值
//带排序的字符串放在s数组中,从s[0] - s[n-1],
//长度为n,且最大值小于m
//除了s[n-1]之外的所有s[i] 都大于0,r[n-1] = 0
//函数结束后,结果放在sa数组中
bool cmp(int *r,int a,int b,int l){
return r[a] == r[b] && r[a+l] == r[b+l];
}
void da(int str[],int sa[],int rank[],int height[],int n,int m){
n++;
int i,j,p,*x = t1,*y = t2;
for(i = 0;i< m ;i ++) c[i] = 0;
for(i=0;i=0;i--) sa[--c[x[i] ]] = i;
for(j=1;j<=n;j<<=1){
p = 0;
//直接利用sa数组排序第二关键字
for(i=n-j;i= j) y[p++] = sa[i] - j;
//这样数组y保存的就是按照第二关键字排序的结果
//基数排序第一关键字
for(i=0;i=0;i--) sa[--c[x[y[i] ]] ] = y[i];
//根据sa和x数组计算新的x数组
swap(x,y);
p = 1;x[ sa[0]] = 0;
for(i=1;i= n) break;
m = p; //下次基数排序的最大值
}
int k = 0;
n--;
for(i=0;i<=n;i++) rank[sa[i] ] = i;
for(i=0;i b) swap(a,b);
return height[askRMQ(a+1,b)];
}
char str[maxn];
int r[maxn];
int sa[maxn];
char s1[maxn],s2[maxn];
int main(){
gets(s1);
gets(s2);
int len = strlen(s1);
strcat(s1,"$");
strcat(s1,s2);
int n = strlen(s1), m = 0;
for(int i=0;i=len) ||
(sa[i-1] >= len && sa[i] < len) )
res = max(res,height[i]);
}
printf("%d\n",res);
}
POJ1743
首先处理数据,将i 和 i-1位置的差值构造新的数组。
然后只要求新数组 最长的不重叠且重复出现的字串的长度。
在我们假设-最长,不重叠,重复-子串的长度为len,那么在一个height数组中有一些hgt[i]会小于len,在这个i左右的两个子串,他们LCP是不可能大于或等于len的,这样,就可以吧height数组看做很多LCP >= len的段,我们在每一段中进行扫描,记录这一段中最大和最小的子串串索引(sa[x]),如果两者之和小于len,说明重叠了,否则就找到了一个可行解。
/*
* POJ 1743 Musical Theme
* 有N(1 <= N <=20000)个音符的序列来表示一首乐曲,每个音符都是1..88范围内的整数,现在要找一个重复的主题。
* “主题”是整个音符序列的一个子串,它需要满足如下条件:
* 1.长度至少为5个音符
* 2.在乐曲中重复出现(可能经过转调,“转调”的意思是主题序列中每个音符都被加上或减去了同一个整数值。)
* 3.重复出现的同一主题不能有公共部分。
*
* 先转化成相邻两项的差值,然后就是找不可重叠重复子串。
* 做法就是二分答案LEN
* 然后根据height值进行分组
*/
#include
#include
#include
#include
#include
#include
const int maxn = 20000 +10;
using namespace std;
int sa[maxn];
int rank[maxn], height[maxn];
int t1[maxn],t2[maxn],c[maxn];
//求sa数组需要的中间变量,不需要赋值
//带排序的字符串放在s数组中,从s[0] - s[n-1],
//长度为n,且最大值小于m
//除了s[n-1]之外的所有s[i] 都大于0,r[n-1] = 0
//函数结束后,结果放在sa数组中
bool cmp(int *r,int a,int b,int l){
return r[a] == r[b] && r[a+l] == r[b+l];
}
void da(int str[],int sa[],int rank[],int height[],int n,int m){
n++;
int i,j,p,*x = t1,*y = t2;
for(i = 0;i< m ;i ++) c[i] = 0;
for(i=0;i=0;i--) sa[--c[x[i] ]] = i;
for(j=1;j<=n;j<<=1){
p = 0;
//直接利用sa数组排序第二关键字
for(i=n-j;i= j) y[p++] = sa[i] - j;
//这样数组y保存的就是按照第二关键字排序的结果
//基数排序第一关键字
for(i=0;i=0;i--) sa[--c[x[y[i] ]] ] = y[i];
//根据sa和x数组计算新的x数组
swap(x,y);
p = 1;x[ sa[0]] = 0;
for(i=1;i= n) break;
m = p; //下次基数排序的最大值
}
int k = 0;
n--;
for(i=0;i<=n;i++) rank[sa[i] ] = i;
for(i=0;iMax)Max=sa[i];
if(Max-Min>=k) return true;
}
}
return false;
}
int main(){
int n;
while(scanf("%d",&n) == 1 && n){
for(int i=0;i0;i--) s[i] = s[i] - s[i-1] + 90;
n--; //减少一个长度
for(int i=0;i
POJ3261
可重叠的K次(大于等于K)最长重复子串
#include
#include
#include
#include
#include
#include
const int maxn = 1000005;
using namespace std;
int sa[maxn];
int rank[maxn], height[maxn];
int t1[maxn],t2[maxn],c[maxn];
//求sa数组需要的中间变量,不需要赋值
//带排序的字符串放在s数组中,从s[0] - s[n-1],
//长度为n,且最大值小于m
//除了s[n-1]之外的所有s[i] 都大于0,r[n-1] = 0
//函数结束后,结果放在sa数组中
bool cmp(int *r,int a,int b,int l){
return r[a] == r[b] && r[a+l] == r[b+l];
}
void da(int str[],int sa[],int rank[],int height[],int n,int m){
n++;
int i,j,p,*x = t1,*y = t2;
for(i = 0;i< m ;i ++) c[i] = 0;
for(i=0;i=0;i--) sa[--c[x[i] ]] = i;
for(j=1;j<=n;j<<=1){
p = 0;
//直接利用sa数组排序第二关键字
for(i=n-j;i= j) y[p++] = sa[i] - j;
//这样数组y保存的就是按照第二关键字排序的结果
//基数排序第一关键字
for(i=0;i=0;i--) sa[--c[x[y[i] ]] ] = y[i];
//根据sa和x数组计算新的x数组
swap(x,y);
p = 1;x[ sa[0]] = 0;
for(i=1;i= n) break;
m = p; //下次基数排序的最大值
}
int k = 0;
n--;
for(i=0;i<=n;i++) rank[sa[i] ] = i;
for(i=0;i b) swap(a,b);
return height[askRMQ(a+1,b)];
}
int arr[20010];
int n,m;
bool fun(int k){
int cnt = 1;
for(int i=2;i<=n;i++){
if(height[i] >= k){
cnt ++;
}else{
cnt = 1;
}
if(cnt >= m)
return true;
}
return false;
}
int main(){
while(scanf("%d%d",&n,&m) !=EOF){
int maxx = -1;
for(int i=0;i>1;
if(fun(mid)){
maxx = mid;
l = mid + 1;
}else{
r = mid - 1;
}
}
printf("%d\n",maxx);
}
}
SPOJ694 不同字串个数
每一个子串一定是某个后缀的前缀,那么问题便等价于求所有后缀之间的不相同的前缀个数。我们按sa的顺序来考虑,当加入sa[k]的时候,sa[k]这个后缀的长度为n-sa[k],那么便有n-sa[k]个前缀,但是由heigh数组可知sa[k]与sa[k-1]有height[k]个前缀是相同的,所以要除去,最终的答案便是sigma(n-sa[k]+height[k])
#include
#include
#include
#include
#include
#include
const int maxn = 50500;
using namespace std;
int sa[maxn];
int rank[maxn], height[maxn];
int t1[maxn],t2[maxn],c[maxn];
//求sa数组需要的中间变量,不需要赋值
//带排序的字符串放在s数组中,从s[0] - s[n-1],
//长度为n,且最大值小于m
//除了s[n-1]之外的所有s[i] 都大于0,r[n-1] = 0
//函数结束后,结果放在sa数组中
bool cmp(int *r,int a,int b,int l){
return r[a] == r[b] && r[a+l] == r[b+l];
}
void da(char str[],int sa[],int rank[],int height[],int n,int m){
n++;
int i,j,p,*x = t1,*y = t2;
for(i = 0;i< m ;i ++) c[i] = 0;
for(i=0;i=0;i--) sa[--c[x[i] ]] = i;
for(j=1;j<=n;j<<=1){
p = 0;
//直接利用sa数组排序第二关键字
for(i=n-j;i= j) y[p++] = sa[i] - j;
//这样数组y保存的就是按照第二关键字排序的结果
//基数排序第一关键字
for(i=0;i=0;i--) sa[--c[x[y[i] ]] ] = y[i];
//根据sa和x数组计算新的x数组
swap(x,y);
p = 1;x[ sa[0]] = 0;
for(i=1;i= n) break;
m = p; //下次基数排序的最大值
}
int k = 0;
n--;
for(i=0;i<=n;i++) rank[sa[i] ] = i;
for(i=0;i
给定一个字符串,求最长回文子串。
算法分析:
穷举每一位,然后计算以这个字符为中心的最长回文子串。注意这里要分两
种情况,一是回文子串的长度为奇数,二是长度为偶数。两种情况都可以转化为
求一个后缀和一个反过来写的后缀的最长公共前缀。具体的做法是:将整个字符
串反过来写在原字符串后面,中间用一个特殊的字符隔开。这样就把问题变为了
求这个新的字符串的某两个后缀的最长公共前缀。(论文)
#include
#include
#include
#include
#include
#include
const int maxn = 2020;
using namespace std;
int sa[maxn];
int rank[maxn], height[maxn];
int t1[maxn],t2[maxn],c[maxn];
//求sa数组需要的中间变量,不需要赋值
//带排序的字符串放在s数组中,从s[0] - s[n-1],
//长度为n,且最大值小于m
//除了s[n-1]之外的所有s[i] 都大于0,r[n-1] = 0
//函数结束后,结果放在sa数组中
bool cmp(int *r,int a,int b,int l){
return r[a] == r[b] && r[a+l] == r[b+l];
}
void da(int str[],int sa[],int rank[],int height[],int n,int m){
n++;
int i,j,p,*x = t1,*y = t2;
for(i = 0;i< m ;i ++) c[i] = 0;
for(i=0;i=0;i--) sa[--c[x[i] ]] = i;
for(j=1;j<=n;j<<=1){
p = 0;
//直接利用sa数组排序第二关键字
for(i=n-j;i= j) y[p++] = sa[i] - j;
//这样数组y保存的就是按照第二关键字排序的结果
//基数排序第一关键字
for(i=0;i=0;i--) sa[--c[x[y[i] ]] ] = y[i];
//根据sa和x数组计算新的x数组
swap(x,y);
p = 1;x[ sa[0]] = 0;
for(i=1;i= n) break;
m = p; //下次基数排序的最大值
}
int k = 0;
n--;
for(i=0;i<=n;i++) rank[sa[i] ] = i;
for(i=0;i b) swap(a,b);
return height[askRMQ(a+1,b)];
}
int r[maxn];
char str[maxn];
int main(){
while(scanf("%s",str)!=EOF){
int len = strlen(str);
int n = 2 * len + 1;
for(int i=0;i ans){
ans = 2*tmp;
st = i - tmp;
}
tmp = lcp(i,n-i-1); // 奇数
if(2 * tmp -1 > ans){
ans = 2 * tmp -1;
st = i - tmp + 1;
}
}
str[st+ans] = 0;
printf("%s\n",str+st);
}
return 0;
}
重复次数最多的连续重复子串(spoj687,pku3693)
这题目是对height数组进行RMQ稍微改了下模板。将RMQ数组换成了height数组
cx_love题解
在后缀数组神文中也这题的题解。
比较容易理解的部分就是枚举长度为L,然后看长度为L的字符串最多连续出现几次。
既然长度为L的串重复出现,那么str[0],str[l],str[2*l]……中肯定有两个连续的出现在字符串中。
那么就枚举连续的两个,然后从这两个字符前后匹配,看最多能匹配多远。
即以str[i*l],str[i*l+l]前后匹配,这里是通过查询suffix(i*l),suffix(i*l+l)的最长公共前缀
通过rank值能找到i*l,与i*l+l的排名,我们要查询的是这段区间的height的最小值,通过RMQ预处理
达到查询为0(1)的复杂度,
设LCP长度为M, 则答案显然为M / L + 1, 但这不一定是最好的, 因为答案的首尾不一定再我们枚举的位置上. 我的解决方法是, 我们考虑M % L的值的意义, 我们可以认为是后面多了M % L个字符, 但是我们更可以想成前面少了(L - M % L)个字符! 所以我们求后缀j * L - (L - M % L)与后缀(j + 1) * L - (L - M % L)的最长公共前缀。
即把之前的区间前缀L-M%L即可。
然后把可能取到最大值的长度L保存,由于 题目要求字典序最小,通过sa数组进行枚举,取到的第一组,肯定是字典序最小的。
#include
#include
#include
#include
#include
#include
const int maxn = 100000 + 10;
using namespace std;
int sa[maxn];
int rank[maxn], height[maxn];
int t1[maxn],t2[maxn],c[maxn];
//求sa数组需要的中间变量,不需要赋值
//带排序的字符串放在s数组中,从s[0] - s[n-1],
//长度为n,且最大值小于m
//除了s[n-1]之外的所有s[i] 都大于0,r[n-1] = 0
//函数结束后,结果放在sa数组中
bool cmp(int *r,int a,int b,int l){
return r[a] == r[b] && r[a+l] == r[b+l];
}
void da(int str[],int sa[],int rank[],int height[],int n,int m){
n++;
int i,j,p,*x = t1,*y = t2;
for(i = 0;i< m ;i ++) c[i] = 0;
for(i=0;i=0;i--) sa[--c[x[i] ]] = i;
for(j=1;j<=n;j<<=1){
p = 0;
//直接利用sa数组排序第二关键字
for(i=n-j;i= j) y[p++] = sa[i] - j;
//这样数组y保存的就是按照第二关键字排序的结果
//基数排序第一关键字
for(i=0;i=0;i--) sa[--c[x[y[i] ]] ] = y[i];
//根据sa和x数组计算新的x数组
swap(x,y);
p = 1;x[ sa[0]] = 0;
for(i=1;i= n) break;
m = p; //下次基数排序的最大值
}
int k = 0;
n--;
for(i=0;i<=n;i++) rank[sa[i] ] = i;
for(i=0;i b) swap(a,b);
return height[askRMQ(a+1,b)];
}
char str[maxn];
int r[maxn];
int a[maxn];
int main(){
int cas= 0;
while(scanf("%s",str) == 1){
if(str[0] == '#') break;
cas++;
int n = strlen(str);
for(int i=0;i<=n;i++) r[i] = str[i];
da(r,sa,rank,height,n,128);
initRMQ(n);
int cnt = 0, maxx = 0;
for(int l=1;l= 0 && t1 % l){
if(lcp(k,k+l) >= t1) step++;
}
if(step > maxx){
maxx = step;
cnt = 0;
a[cnt++] = l;
}else if(step == maxx)
a[cnt++] = l;
}
}
int len = -1,st;
for(int i=1;i<=n&&len == -1 ; i++){
for(int j=0;j= (maxx-1)*l ){
len = l;
st = sa[i];
break;
}
}
}
// cout<
POJ3415
长度不小于 k 的公共子串的个数(pku3415)
给定两个字符串 A 和 B,求长度不小于 k 的公共子串的个数(可以相同)。
样例 1:
A=“xx”,B=“xx”,k=1,长度不小于 k 的公共子串的个数是 5。
样例 2:
A=“aababaa”,B=“abaabaa”,k=2,长度不小于 k 的公共子串的个数是22。
算法分析:
基本思路是计算 A 的所有后缀和 B 的所有后缀之间的最长公共前缀的长度,
把最长公共前缀长度不小于 k 的部分全部加起来。先将两个字符串连起来,中间
用一个没有出现过的字符隔开。按 height 值分组后,接下来的工作便是快速的
统计每组中后缀之间的最长公共前缀之和。扫描一遍,每遇到一个 B 的后缀就统
计与前面的 A 的后缀能产生多少个长度不小于 k 的公共子串,这里 A 的后缀需要
用一个单调的栈来高效的维护。然后对 A 也这样做一次。具体的细节留给读者思
考。
/*
* POJ 3415 Common Substrings
* 给定两个字符串A和B,求长度不小于k的公共子串的个数
* 基本思路是计算A的所有后缀和B的所有后缀之间的最长公共前缀的长度,
* 把最长公共前缀长度不小于k的部分全部加起来。
* 先把两个字符串连起来,中间用一个没有用过的字符隔开。
* 按height分组后,接下来便是快速的统计每组中后缀之间的最长公共前缀之和
* 用一个单调的栈来维护,每遇到一个B的后缀就统计与前面的A的后缀
* 能产生多少个长度不小于k的公共子串。最A也一样做一边
*/
#include
#include
#include
#include
#include
#include
const int maxn = 200000 + 10;
using namespace std;
int sa[maxn];
int rank[maxn], height[maxn];
int t1[maxn],t2[maxn],c[maxn];
//求sa数组需要的中间变量,不需要赋值
//带排序的字符串放在s数组中,从s[0] - s[n-1],
//长度为n,且最大值小于m
//除了s[n-1]之外的所有s[i] 都大于0,r[n-1] = 0
//函数结束后,结果放在sa数组中
bool cmp(int *r,int a,int b,int l){
return r[a] == r[b] && r[a+l] == r[b+l];
}
void da(int str[],int sa[],int rank[],int height[],int n,int m){
n++;
int i,j,p,*x = t1,*y = t2;
for(i = 0;i< m ;i ++) c[i] = 0;
for(i=0;i=0;i--) sa[--c[x[i] ]] = i;
for(j=1;j<=n;j<<=1){
p = 0;
//直接利用sa数组排序第二关键字
for(i=n-j;i= j) y[p++] = sa[i] - j;
//这样数组y保存的就是按照第二关键字排序的结果
//基数排序第一关键字
for(i=0;i=0;i--) sa[--c[x[y[i] ]] ] = y[i];
//根据sa和x数组计算新的x数组
swap(x,y);
p = 1;x[ sa[0]] = 0;
for(i=1;i= n) break;
m = p; //下次基数排序的最大值
}
int k = 0;
n--;
for(i=0;i<=n;i++) rank[sa[i] ] = i;
for(i=0;i 0 && height[i] <= sta[top-1]){
top--;
ss -= stb[top] * (sta[top] - height[i]);
cnt += stb[top];
}
sta[top] = height[i];
stb[top++] = cnt;
if(sa[i] > len1) ans += ss;
}
ss = 0; top = 0;
for(int i=2;i<=n;i++){
if(height[i] < k){
ss = 0;
top = 0;
continue;
}
int cnt = 0;
if(sa[i - 1] > len1){
cnt++;
ss += height[i] - k + 1;
}
while(top > 0 && height[i] <= sta[top - 1]){
top --;
ss -= stb[top] * (sta[top] - height[i]);
cnt += stb[top];
}
sta[top] = height[i];
stb[top++] = cnt;
if(sa[i] < len1) ans += ss;
}
printf("%lld\n",ans);
}
return 0;
}
/*
* poj 3294
* 给出n个字符串,求出现在一半以上字符串的最长子串,按照字典序输出所有结果
* 将n个字符串连接起来,中间用没有出现过的字符隔开,然后求后缀数组。
然后二分答案,进行分组,判断每组的后缀是否出现在不少于k个的原串中,
*/
#include
#include
#include
#include
#include
#include
const int maxn = 200000 + 10;
using namespace std;
int sa[maxn];
int rank[maxn], height[maxn];
int t1[maxn],t2[maxn],c[maxn];
//求sa数组需要的中间变量,不需要赋值
//带排序的字符串放在s数组中,从s[0] - s[n-1],
//长度为n,且最大值小于m
//除了s[n-1]之外的所有s[i] 都大于0,r[n-1] = 0
//函数结束后,结果放在sa数组中
bool cmp(int *r,int a,int b,int l){
return r[a] == r[b] && r[a+l] == r[b+l];
}
void da(int str[],int sa[],int rank[],int height[],int n,int m){
n++;
int i,j,p,*x = t1,*y = t2;
for(i = 0;i< m ;i ++) c[i] = 0;
for(i=0;i=0;i--) sa[--c[x[i] ]] = i;
for(j=1;j<=n;j<<=1){
p = 0;
//直接利用sa数组排序第二关键字
for(i=n-j;i= j) y[p++] = sa[i] - j;
//这样数组y保存的就是按照第二关键字排序的结果
//基数排序第一关键字
for(i=0;i=0;i--) sa[--c[x[y[i] ]] ] = y[i];
//根据sa和x数组计算新的x数组
swap(x,y);
p = 1;x[ sa[0]] = 0;
for(i=1;i= n) break;
m = p; //下次基数排序的最大值
}
int k = 0;
n--;
for(i=0;i<=n;i++) rank[sa[i] ] = i;
for(i=0;i b) swap(a,b);
return height[askRMQ(a+1,b)];
}
int n;
char str[110][1010];
int st[110], ed[110] ; // 各个字符串对应的起始和结束
bool used[110]; // 标记
int who[maxn];
int r[maxn];
int check(int totlen,int len, int k){
memset(used,0,sizeof used);
int ret = 0;
int tmp = who[ sa[1]];
if(tmp != -1 && used[tmp] == false){
ret++;
used[tmp] = true;
}
if(ret >= k) return 1;
for(int i=2;i<=totlen;i++){
if(height[i] < len){
ret = 0;
memset(used,false,sizeof used);
tmp = who[sa[i] ];
if(tmp != -1 && used[ tmp] == false){
ret ++;
used[tmp] = true;
}
if(ret >=k) return i;
}else{
tmp = who[sa[i] ];
if(tmp != -1 && used[tmp] == false){
ret++;
used[tmp] = true;
}
if(ret >= k) return i;
}
}
return -1;
}
void output(int totlen, int len, int k){
memset(used,0,sizeof used);
int ret = 0;
int tmp = who[sa[1] ];
if(tmp != 1 && used[tmp] == false){
ret++;
used[tmp] = true;
}
for(int i=2;i<=totlen;i++){
if(height[i] < len){
if(ret >= k){
for(int j=0;j= k){
for(int j=0;j
int mid = (left + right) >> 1; int x = check(totlen,mid,k); if(x == -1){ right = mid - 1; }else{ ans = mid; left = mid + 1; } } if(ans <= 0) printf("?\n"); else{ output(totlen,ans,k); } } return 0;}
spoj220
/*
给定 n 个字符串,求在每个字符串中至少出现两次且不重叠的最长子串。
算法分析:
做法和上题大同小异,也是先将 n 个字符串连起来,中间用不相同的且没有
出现在字符串中的字符隔开,求后缀数组。然后二分答案,再将后缀分组。判断
的时候,要看是否有一组后缀在每个原来的字符串中至少出现两次,并且在每个
原来的字符串中,后缀的起始位置的最大值与最小值之差是否不小于当前答案
(判断能否做到不重叠,如果题目中没有不重叠的要求,那么不用做此判断)。
这个做法的时间复杂度为 O(nlogn)。
*/
#include
#include
#include
#include
#include
#include
const int maxn = 200000 + 10;
using namespace std;
const int INF = 0x3f3f3f3f;
int sa[maxn];
int rank[maxn], height[maxn];
int t1[maxn],t2[maxn],c[maxn];
//求sa数组需要的中间变量,不需要赋值
//带排序的字符串放在s数组中,从s[0] - s[n-1],
//长度为n,且最大值小于m
//除了s[n-1]之外的所有s[i] 都大于0,r[n-1] = 0
//函数结束后,结果放在sa数组中
bool cmp(int *r,int a,int b,int l){
return r[a] == r[b] && r[a+l] == r[b+l];
}
void da(int str[],int sa[],int rank[],int height[],int n,int m){
n++;
int i,j,p,*x = t1,*y = t2;
for(i = 0;i< m ;i ++) c[i] = 0;
for(i=0;i=0;i--) sa[--c[x[i] ]] = i;
for(j=1;j<=n;j<<=1){
p = 0;
//直接利用sa数组排序第二关键字
for(i=n-j;i= j) y[p++] = sa[i] - j;
//这样数组y保存的就是按照第二关键字排序的结果
//基数排序第一关键字
for(i=0;i=0;i--) sa[--c[x[y[i] ]] ] = y[i];
//根据sa和x数组计算新的x数组
swap(x,y);
p = 1;x[ sa[0]] = 0;
for(i=1;i= n) break;
m = p; //下次基数排序的最大值
}
int k = 0;
n--;
for(i=0;i<=n;i++) rank[sa[i] ] = i;
for(i=0;i b) swap(a,b);
return height[askRMQ(a+1,b)];
}
char ch[maxn];
int str[maxn];
int l[105];
int mx[15], mn[15];
int in[maxn];
int k;
bool check(int mid,int n){
for(int i=0;i
HDU4552
/*
HDU 4552 怪盗基德的挑战书(后缀数组)
题目就是求前缀出现的次数。
用后缀数组求的话,就是求出每个后缀和最长的后缀的公共前缀长度就可以了。
就是rank[0]的位置往两边找。
这题数据很水,暴力都可过。
用KMP做也很简单
*/
#include
#include
#include
#include
#include
#include
const int maxn = 200000 + 10;
using namespace std;
const int INF = 0x3f3f3f3f;
int sa[maxn];
int Rank[maxn], height[maxn];
int t1[maxn],t2[maxn],c[maxn];
//求sa数组需要的中间变量,不需要赋值
//带排序的字符串放在s数组中,从s[0] - s[n-1],
//长度为n,且最大值小于m
//除了s[n-1]之外的所有s[i] 都大于0,r[n-1] = 0
//函数结束后,结果放在sa数组中
bool cmp(int *r,int a,int b,int l){
return r[a] == r[b] && r[a+l] == r[b+l];
}
void da(int str[],int sa[],int Rank[],int height[],int n,int m){
n++;
int i,j,p,*x = t1,*y = t2;
for(i = 0;i< m ;i ++) c[i] = 0;
for(i=0;i=0;i--) sa[--c[x[i] ]] = i;
for(j=1;j<=n;j<<=1){
p = 0;
//直接利用sa数组排序第二关键字
for(i=n-j;i= j) y[p++] = sa[i] - j;
//这样数组y保存的就是按照第二关键字排序的结果
//基数排序第一关键字
for(i=0;i=0;i--) sa[--c[x[y[i] ]] ] = y[i];
//根据sa和x数组计算新的x数组
swap(x,y);
p = 1;x[ sa[0]] = 0;
for(i=1;i= n) break;
m = p; //下次基数排序的最大值
}
int k = 0;
n--;
for(i=0;i<=n;i++) Rank[sa[i] ] = i;
for(i=0;i b) swap(a,b);
return height[askRMQ(a+1,b)];
}
char str[maxn];
int s[maxn];
/*
suffix(j)和suffix(k)的最长公共前缀为height[rank[j]+1],height[rank[j]+2],height[rank[j]+3],……,height[rank[k]]中的最小值。
由此从第一个字符的后缀开始,依照后缀排名的顺序左右扫描
不太明白。
*/
int main(){
while(scanf("%s",str) == 1){
int n = strlen(str);
for(int i=0;i<=n;i++) s[i] =str[i];
da(s,sa,Rank,height,n,128);
int ans = n;
int t = Rank[0];
int tmp = n;
while(t < n){
tmp = min(tmp, height[ t + 1]);
t++;
ans += tmp;
}
t =Rank[0];
tmp = n;
while(t > 1){
tmp = min(tmp,height[t]);
t--;
ans += tmp;
}
printf("%d\n",ans % 256);
}
return 0;
}
HDU4691 求最长公共前缀
/*
HDU4691 求最长公共前缀
*/
#include
#include
#include
#include
#include
#include
const int maxn = 100000 + 10;
using namespace std;
const int INF = 0x3f3f3f3f;
int sa[maxn];
int Rank[maxn], height[maxn];
int t1[maxn],t2[maxn],c[maxn];
//求sa数组需要的中间变量,不需要赋值
//带排序的字符串放在s数组中,从s[0] - s[n-1],
//长度为n,且最大值小于m
//除了s[n-1]之外的所有s[i] 都大于0,r[n-1] = 0
//函数结束后,结果放在sa数组中
bool cmp(int *r,int a,int b,int l){
return r[a] == r[b] && r[a+l] == r[b+l];
}
void da(int str[],int sa[],int Rank[],int height[],int n,int m){
n++;
int i,j,p,*x = t1,*y = t2;
for(i = 0;i< m ;i ++) c[i] = 0;
for(i=0;i=0;i--) sa[--c[x[i] ]] = i;
for(j=1;j<=n;j<<=1){
p = 0;
//直接利用sa数组排序第二关键字
for(i=n-j;i= j) y[p++] = sa[i] - j;
//这样数组y保存的就是按照第二关键字排序的结果
//基数排序第一关键字
for(i=0;i=0;i--) sa[--c[x[y[i] ]] ] = y[i];
//根据sa和x数组计算新的x数组
swap(x,y);
p = 1;x[ sa[0]] = 0;
for(i=1;i= n) break;
m = p; //下次基数排序的最大值
}
int k = 0;
n--;
for(i=0;i<=n;i++) Rank[sa[i] ] = i;
for(i=0;i b) swap(a,b);
return height[askRMQ(a+1,b)];
}
char str[maxn];
int r[maxn];
int A[maxn], B[maxn];
int calc(int n){
if(n == 0) return 1;
int ret = 0;
while(n){
ret++;
n /= 10;
}
return ret;
}
typedef long long ll;
int main(){
while(scanf("%s",str) == 1){
int n = strlen(str);
for(int i=0;i
height 数组:定义 height[i]=suffix(sa[i-1])和 suffix(sa[i])的最长公
共前缀,也就是排名相邻的两个后缀的最长公共前缀。那么对于 j 和 k,不妨设
rank[j]
height[rank[j]+2], height[rank[j]+3], ... ,height[rank[k]]中的最小值。
后缀数组:后缀数组 SA 是一个一维数组,它保存 1..n 的某个排列 SA[1],
SA[2],......,SA[n],并且保证 Suffix(SA[i]) < Suffix(SA[i+1]),1≤i
次放入 SA 中。
名次数组:名次数组 Rank[i]保存的是 Suffix(i)在所有后缀中从小到大排
列的“名次”。
简单的说,后缀数组是“排第几的是谁?”,名次数组是“你排第几?”。容
易看出,后缀数组和名次数组为互逆运算。