POJ 3415 Common Substrings ( 后缀数组+单调栈(那篇论文中的题目。多谢罗神给的的思路)

Common Substrings
Time Limit: 5000MS Memory Limit: 65536K
Total Submissions: 9781 Accepted: 3222
Description

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

鸣谢:http://blog.csdn.net/acm_cxlove/article/details/7946967
因为这篇博客的思路我才能有所了解。

题意大概是:
求出两个字符串中不少于k个公共子串的个数

单调队列就是指队列中保证队列里的元素是单调的。

思路就是: 先将两个字符串连接成一个字符串,并在中间用一个未出现的字符将其隔开,并在末尾用一个字符 ‘\0’ 将其结尾(这是论文中说的以0位结尾)
在求出后缀数组之后,从头到尾扫描一遍,top表示为栈顶下标。
用tot 记录当前height所能为答案贡献的长度。
如 height[3]=4,k=2,那么height[3]所能贡献的长度为 4-2+1=3

但是如果过程中出现了小于k的情况那么将tot,top归零。
如果出现了当前的height{i]与栈中的值小的话,难么就减去那个差值。
s[top][0]表示上一个height[]元素的值,s[top][1]表示上次的cnt的值

大家可以手写一遍check()函数的过程就大概理解了。(不要问我是怎么知道的……..我就是这么干的。。。。。。。。。)

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include <fstream>
#include<cmath>
#define N 100005
#define LL long long
#define maxn 200005
#define MME(i,j) memset(i,j,sizeof(i))
using namespace std;
//以下为倍增算法求后缀数组
int wa[maxn],wb[maxn],wv[maxn],Ws[maxn];
int cmp(int *r,int a,int b,int l)
{
    return r[a]==r[b]&&r[a+l]==r[b+l];
}
void da(const char *r,int *sa,int n,int m)
{
    int i,j,p,*x=wa,*y=wb,*t;
    for(i=0; i<m; i++) Ws[i]=0;
    for(i=0; i<n; i++) Ws[x[i]=r[i]]++;
    for(i=1; i<m; i++) Ws[i]+=Ws[i-1];
    for(i=n-1; i>=0; i--) sa[--Ws[x[i]]]=i;
    for(j=1,p=1; p<n; j*=2,m=p)
    {
        for(p=0,i=n-j; i<n; i++) y[p++]=i;
        for(i=0; i<n; i++) if(sa[i]>=j) y[p++]=sa[i]-j;
        for(i=0; i<n; i++) wv[i]=x[y[i]];
        for(i=0; i<m; i++) Ws[i]=0;
        for(i=0; i<n; i++) Ws[wv[i]]++;
        for(i=1; i<m; i++) Ws[i]+=Ws[i-1];
        for(i=n-1; i>=0; i--) sa[--Ws[wv[i]]]=y[i];
        for(t=x,x=y,y=t,p=1,x[sa[0]]=0,i=1; i<n; i++)
            x[sa[i]]=cmp(y,sa[i-1],sa[i],j)?p-1:p++;
    }

  /*  ofstream file("SA.txt");
    for(int i=0; i<n; i++)
        file<<"SA["<<i<<"] is "<<sa[i]<<"\n";

    file.close();

    for(int i=0; i<=n; i++)
        printf("SA[%d] is %d\n",i,sa[i]);*/
    return;

}
int sa[maxn],Rank[maxn],height[maxn];
//求height数组
void calheight(const char *r,int *sa,int n)
{
    int i,j,k=0;
    for(i=1; i<=n; i++) Rank[sa[i]]=i;
    for(i=0; i<n; height[Rank[i++]]=k)
        for(k?k--:0,j=sa[Rank[i]-1]; r[i+k]==r[j+k]; k++);

   /* for(int i=0; i<=n; i++)
        printf("h[%d] is %d\n",i,height[i]);*/

    return;
}
char str[maxn],ch[maxn];
int k;
int s[maxn][2];
LL tot,top;

LL check(int len,int lenA)
{
    tot=top=0;
    MME(s,0);
    LL sum=0;
    for(int i=1; i<=len; i++)
    {
        if(height[i]<k) tot=top=0;
        else
        {
            int cnt=0;
            if(sa[i-1]<lenA) cnt++, tot+=height[i]-k+1;
            while(top>0&&height[i]<=s[top-1][0])
            {
                top--;
                tot-= s[top][1]*(s[top][0]-height[i]);
                cnt+= s[top][1];
            }
            s[top][0]=height[i],s[top++][1]=cnt;
            if(sa[i]>lenA)
                sum+=tot;
        }
    }
    tot=top=0;
    for(int i=1; i<=len; i++)
    {
        if(height[i]<k) tot=top=0;
        else
        {
            int cnt=0;
            if(sa[i-1]>lenA) cnt++,tot+=height[i]-k+1;
            while(top>0&&height[i]<=s[top-1][0])
            {
                top--;
                tot-= s[top][1]*(s[top][0]-height[i]);
                cnt+= s[top][1];
            }
            s[top][0]=height[i],s[top++][1]=cnt;
            if(sa[i]<lenA)
                sum+=tot;
        }
    }
    return sum;
}
int main()
{
    while(scanf("%d",&k)!=EOF&&k)
    {
        int l1,l2;
        scanf("%s%s",str,ch);

        l1=strlen(str);
        l2=strlen(ch);
        str[l1]='@';
        for(int i=l1+1; i<=l1+l2; i++)
            str[i]=ch[i-l1-1];
        int n=l1+l2+1;
        str[n]='\0';

        da(str,sa,n+1,130);
        calheight(str,sa,n);

        LL sum;
        sum=check(n,l1);
        printf("%I64d\n",sum);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值