spoj-3415(后缀数组+单调栈)

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;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值