spoj-3415(后缀数组+单调栈)
Common Substrings
A substring of a string T is defined as:
T( i, k)= TiTi +1… Ti+k -1, 1≤ i≤ i+k-1≤| T|.
Given two strings A, B and one integer K, we define S, a set of triples (i, j, k):
S = {( i, j, k) | k≥ K, A( i, k)= B( j, k)}.
You are to give the value of |S| for specific A, B and K.
Input
The input file contains several blocks of data. For each block, the first line contains one integer K, followed by two lines containing strings A and B, respectively. The input file is ended by K=0.
1 ≤ |A|, |B| ≤ 105
1 ≤ K ≤ min{|A|, |B|}
Characters of A and B are all Latin letters.
Output
For each case, output an integer |S|.
Sample Input
2
aababaa
abaabaa
1
xx
xx
0
Sample Output
22
5
题意是给两个字符串和k,要求找到从a串i位置和b串j位置开始的x(x>=k)个字符相等,问有多少个这样的(i,j).
后缀数组的论文题,把两个字符串连起来,中间用特殊字符隔开,套后缀数组模板,得出sa和height。然后根据k把height数组分段,对于每一段,相同前缀都是大于k的。然后从头到尾遍历,如果是a串的就放进栈里,b串的就计算之前的栈内能匹配的数目。需要借助一个单调栈保证栈内所有元素都是从小到大的,用maxn记录栈内所有的之的和,如果进来一个比栈顶元素小的数,则把栈内元素pop,直到栈内不存在比这个数大的数。然后在反过来b串匹配a串一遍即可。
具体情形需要仔细想一想,沥青逻辑关系就可以了。
AC代码:
#include<iostream>
#include<algorithm>
#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<vector>
#include<cmath>
#include<complex>
#include<queue>
#include <stack>
#define T 211111
using namespace std;
char s[T];
int t1[T],t2[T],cc[T],x[T],sa[T],rnk[T],height[T];
int len,n;
int ans,k;
int sta[T][2];
bool cmp(int *y,int a,int b,int k)
{
int a1=y[a];
int b1=y[b];
int a2=a+k>=len ? -1:y[a+k];
int b2=b+k>=len ? -1:y[b+k];
return a1==b1 && a2==b2;
}
int make_sa()
{
int *x=t1,*y=t2;
int m=255;
for(int i=0; i<m; i++) cc[i]=0;
for(int i=0; i<len; i++) ++cc[x[i]=s[i]];
for(int i=1; i<m; i++) cc[i]+=cc[i-1];
for(int i=len-1; i>=0; i--) sa[--cc[x[i]]]=i;
for(int k=1; k<=len; k<<=1)
{
int p=0;
for(int i=len-k; i<len; i++) y[p++]=i;
for(int i=0; i<len; i++)
if( sa[i]>=k ) y[p++]=sa[i]-k;
for(int i=0; i<m; i++) cc[i]=0;
for(int i=0; i<len; i++) ++cc[x[y[i]]];
for(int i=1; i<m; i++) cc[i]+=cc[i-1];
for(int i=len-1; i>=0; i--) sa[--cc[x[y[i]]]]=y[i];
swap(x,y);
m=1; x[sa[0]]=0;
for(int i=1; i<len; i++)
x[sa[i]]=cmp(y,sa[i],sa[i-1],k) ? m-1:m++;
if( m>=len ) break;
}
}
void make_height()
{
for(int i=0; i<len; i++) rnk[sa[i]]=i;
height[0]=0;
int k=0;
for(int i=0; i<len; i++)
{
if(!rnk[i]) continue;
int j=sa[rnk[i]-1];
if(k) k--;
while(s[i+k]==s[j+k]) k++;
height[rnk[i]]=k;
}
}
bool isok(int is,int x)
{
if(is==1){
if(x>n) return 1;
else return 0;
}
else{
if(x>n) return 0;
else return 1;
}
}
long long cal(int is,int k)
{
long long maxn=0,pos=-1,now=0,x;
for(int i=1;i<=len;i++){
if(isok(is,sa[i-1])){
maxn+=now;
if(pos==-1) x=-1;
else x=sta[pos][0];
if(height[i]<k){
pos=-1;
now=0;
continue;
}
if(height[i]<x&&i!=len){
int num=0;
while(sta[pos][0]>=height[i]){
num+=sta[pos][1];
now-=sta[pos][1]*(sta[pos][0]-k+1);
pos--;
}
sta[++pos][0]=height[i];
sta[pos][1]=num;
now+=num*(height[i]-k+1);
}
}
else{
if(i==len||height[i]<k){
pos=-1;
now=0;
continue;
}
if(pos==-1) x=-1;
else x=sta[pos][0];
if(height[i]>x){
sta[++pos][0]=height[i];
sta[pos][1]=1;
now+=(height[i]-k+1);
}
else {
int num=1;
while(sta[pos][0]>=height[i]&&pos>=0){
num+=sta[pos][1];
now-=sta[pos][1]*(sta[pos][0]-k+1);
pos--;
}
sta[++pos][0]=height[i];
sta[pos][1]=num;
now+=num*(height[i]-k+1);
}
}
}
return maxn;
}
int main()
{
while(scanf("%d",&k)!=EOF&&k){
scanf("%s",s);
n=strlen(s);
s[n]='&';
scanf("%s",s+n+1);
len=strlen(s);
make_sa();
make_height();
long long int maxn=0;
maxn+=cal(1,k);
maxn+=cal(0,k);
printf("%lld\n",maxn);
}
return 0;
}