zoukankan      html  css  js  c++  java
  • poj3415 Common Substrings (后缀数组+单调队列)

    Time Limit: 5000MS   Memory Limit: 65536K
    Total Submissions: 9414   Accepted: 3123

    Description

    A substring of a string T is defined as:

    T(ik)=TiTi+1...Ti+k-1, 1≤ii+k-1≤|T|.

    Given two strings AB and one integer K, we define S, a set of triples (ijk):

    S = {(ijk) | kKA(ik)=B(jk)}.

    You are to give the value of |S| for specific AB 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

    题意:给你2个字符串,让你分别在两个字符串中找到一个相同的子串,并且这两个相同串的长度要大于等于k,求这样的子串个数。

    思路:这题思路不好想啊。我们知道,一个字符串的子串可以由母串的一个后缀的前缀表示,所以题目就变为求A中所有后缀的前缀和B中所有后缀的前缀公共前缀长度大于等于k的对数。我们可以把两个串连接起来,并且在两个串的中间插入一个以前没有出现过的字符(注意:这个字符的大小不能是0,即不能和最后一个我们自己添加的字符的大小相同,不然会错 = =),然后先求出sa[],height[]。按height[]值分组后,接下来的工作便是快速的统计每组中后缀之间的最长公共前缀之和。扫描一遍,每遇到一个B的后缀就统计与前面的A 的后缀能产生多少个长度不小于k 的公共子串,这里A 的后缀需要用一个单调的栈来高效的维护,然后对A也这样做一次。

    #include<iostream>
    #include<stdio.h>
    #include<stdlib.h>
    #include<string.h>
    #include<math.h>
    #include<vector>
    #include<map>
    #include<set>
    #include<string>
    #include<bitset>
    #include<algorithm>
    using namespace std;
    #define lson th<<1
    #define rson th<<1|1
    typedef long long ll;
    typedef long double ldb;
    #define inf 99999999
    #define pi acos(-1.0)
    #define M 100050
    #define maxn 200050
    char s1[M],s2[M];
    int sa[maxn],a[maxn];
    int wa[maxn],wb[maxn],wv[maxn],we[maxn];
    int rk[maxn],height[maxn];
    int cmp(int *r,int a,int b,int l){
        return r[a]==r[b]&&r[a+l]==r[b+l];
    }
    void build_sa(int *r,int n,int m)
    {
        int i,j,p,*x=wa,*y=wb,*t;
        for(i=0;i<m;i++)we[i]=0;
        for(i=0;i<n;i++)we[x[i]=r[i]]++;
        for(i=1;i<m;i++)we[i]+=we[i-1];
        for(i=n-1;i>=0;i--)sa[--we[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++)we[i]=0;
            for(i=0;i<n;i++)we[wv[i]]++;
            for(i=1;i<m;i++)we[i]+=we[i-1];
            for(i=n-1;i>=0;i--)sa[--we[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++;
        }
    }
    
    void calheight(int *r,int n)
    {
        int i,j,k=0;
        for(i=1;i<=n;i++)rk[sa[i]]=i;
        for(i=0;i<n;height[rk[i++] ]=k){
            for(k?k--:0,j=sa[rk[i]-1];r[i+k]==r[j+k];k++);
        }
    }
    int q[111111][3];  //0表示高度,1表示宽度,2表示时间
    
    int main()
    {
        int n,m,i,j,k;
        int front,rear,len1,len2;
        ll sum,kuan,tot; //sum表示最后的答案,kuan表示单调队列里面每一个元素的宽度
        while(scanf("%d",&k)!=EOF && k!=0)
        {
            scanf("%s%s",s1,s2);
            n=0;
            len1=strlen(s1);
            len2=strlen(s2);
            for(i=0;i<len1;i++){
                a[n++]=s1[i]-'a'+98;
            }
            a[n++]=1;
            for(i=0;i<len2;i++){
                a[n++]=s2[i]-'a'+98;
            }
            a[n]=0;
            build_sa(a,n+1,130);
            calheight(a,n);
    
    
            sum=0;
            for(i=1;i<=n;i++){
                if(height[i]<k){
                    front=1,rear=0;
                    tot=0;   //tot表示所有面积和
                    continue;
                }
                kuan=0;
                while(front<=rear && q[rear][0]>=height[i]){
                    kuan+=q[rear][1];
                    tot-=q[rear][1]*(q[rear][0]-height[i]);
                    rear--;
                }
                if(sa[i-1]<len1){
                    kuan++;
                    tot+=height[i]-k+1;
                }
                rear++;
                q[rear][0]=height[i];q[rear][1]=kuan;
                if(sa[i]>len1){
                    sum+=tot;
                }
            }
    
            for(i=1;i<=n;i++){
                if(height[i]<k){
                    front=1,rear=0;
                    tot=0;   //tot表示所有面积和
                    continue;
                }
                kuan=0;
                while(front<=rear && q[rear][0]>=height[i]){
                    kuan+=q[rear][1];
                    tot-=q[rear][1]*(q[rear][0]-height[i]);
                    rear--;
                }
                if(sa[i-1]>len1){
                    kuan++;
                    tot+=height[i]-k+1;
                }
                rear++;
                q[rear][0]=height[i];q[rear][1]=kuan;
                if(sa[i]<len1){
                    sum+=tot;
                }
            }
            printf("%lld
    ",sum);
        }
        return 0;
    }
    


  • 相关阅读:
    Java 注解(Annotation)
    定时任务相关介绍
    Linux基础命令yum
    Linux基础命令rpm
    Linux基础命令date(如何设置时间? 如何同步时间?)
    Linux基础命令tar(如何压缩文件?如何解压文件?如何不解压查看内容?)
    Linux基础命令gzip
    Linux基础命令zip unzip (压缩 解压)
    Linux中压缩的概念(什么是压缩包?)
    Linux基础命令练习答案7.27
  • 原文地址:https://www.cnblogs.com/herumw/p/9464489.html
Copyright © 2011-2022 走看看