poj 3415 后缀数组+单调栈||后缀自动机

来源:互联网 发布:美中国际 知乎 编辑:程序博客网 时间:2024/06/03 20:13

Common Substrings
Time Limit: 5000MS Memory Limit: 65536K
Total Submissions: 11519 Accepted: 3814
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
问两个串大于等于k的长度的公共子串个数
后缀数组拼起来,
一个很明显的想法是一个块区间的首和尾的最大lcp是之间的最小值,所以对于每个属于B串的子串,我们要找出前面所有的A串和它的公共子串且大于等于k的个数,这样每个串的贡献应该是每个A串和B串和这个串公共子串,lcp-k+1,如何求每个A串和当前B串的lcp呢,暴力的话是on^2,不可行,由于开始我们把大于k的都放在一个块里面,所以在一个块里面,必然每个A都会对下面的B有贡献的,很明显我们是可以o1算出每个A串的贡献的 sum+=height数组-k+1,但是这是算到最大的贡献,很有可能当你遇到这个B的时候这两个公共lcp 比height数组小很多,所以使用单调栈,因为两者的lcp只会越来越小,把这些A串的height存在栈里面,如果这个height比当前的大,那么证明这个贡献必须减少,所以

        while(tail>0&&st[tail]>height[i])        {            sum-=(1ll*st[tail]-k+1)*cnt[tail];            sum+=(1ll*height[i]-k+1)*cnt[tail];            num+=cnt[tail];            tail--;        }

我们同时用cnt数组记录这个串承载了多少了个串的本事,因为他们的贡献将变一样,所以就可以完全弹走了,而算A串的时候,B串没有初始化为num=1,所以必然不会有B串的贡献,很很很巧妙

#include <iostream>#include <cstdio>#include <algorithm>#include <cstring>using namespace std;const int MAXN = 2e5+500;  int t1[MAXN],t2[MAXN],c[MAXN];  int len1,len2;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 ra[],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<n;i++) c[x[i]=str[i]]++;      for(i=1;i<m;i++) c[i]+=c[i-1];      for(i=n-1;i>=0;i--) sa[--c[x[i]]]=i;      for(j=1;j<=n;j<<=1)      {          p=0;          for(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<m;i++) c[i]=0;          for(i=0;i<n;i++) c[x[y[i]]]++;          for(i=1;i<m;i++) c[i]+=c[i-1];          for(i=n-1;i>=0;i--) sa[--c[x[y[i]]]]=y[i];          swap(x,y);          p=1;x[sa[0]]=0;          for(i=1;i<n;i++)              x[sa[i]]=cmp(y,sa[i-1],sa[i],j)?p-1:p++;          if(p>=n) break;          m=p;      }      int k=0;      n--;      for(i=0;i<=n;i++) ra[sa[i]]=i;      for(i=0;i<n;i++)       {          if(k) k--;          j=sa[ra[i]-1];          while(str[i+k]==str[j+k])k++;          height[ra[i]]=k;      }  }  int ra[MAXN],height[MAXN];  int sa[MAXN],num[MAXN];  char str[MAXN];int st[MAXN];typedef long long ll;ll cnt[MAXN];ll solve(int k,int n){    int tail=0;    ll sum=0,ans=0;    for(int i=1;i<=n;i++)    {        if(height[i]<k)         {            sum=0;            tail=0;            continue;        }        ll num=0;        while(tail>0&&st[tail]>height[i])        {            sum-=(1ll*st[tail]-k+1)*cnt[tail];            sum+=(1ll*height[i]-k+1)*cnt[tail];            num+=cnt[tail];            tail--;        }        st[++tail]=height[i];        if(sa[i-1]<len1)        {            sum+=(1ll*height[i]-k+1);            cnt[tail]=num+1;        }        else cnt[tail]=num;        if(sa[i]>len1) ans+=sum;    }    tail=0;    sum=0;    for(int i=1;i<=n;i++)    {        if(height[i]<k)         {            sum=0;            tail=0;            continue;        }        ll num=0;        while(tail>0&&st[tail]>height[i])        {            sum-=(1ll*st[tail]-k+1)*cnt[tail];            sum+=(1ll*height[i]-k+1)*cnt[tail];            num+=cnt[tail];            tail--;        }        st[++tail]=height[i];        if(sa[i-1]>len1)        {            sum+=(1ll*height[i]-k+1);            cnt[tail]=num+1;        }        else cnt[tail]=num;        if(sa[i]<len1)            ans+=sum;    }    return ans;}int main(){        int k;        while(~scanf("%d",&k))        {            if(!k) break;            scanf("%s",str);            len1=strlen(str);            str[len1]='#';            scanf("%s",str+len1+1);            len2=strlen(str);            da(str,sa,ra,height,len2,130);            printf("%lld\n",solve(k,len2) );        }}

Max 数组 能接受的字符串的最长长度
Min 数组 能接受的字符串的最小长度 和Max[fail[p]] 一样
right 能接受的字符串在原串中出现位置的集合
sz right的大小
Max-Min+1 就是每个状态的子串的数目

后缀自动机的做法就是,利用寻找最长公共子串这个匹配,遇到这个点,如果最大匹配长度大于等于k,那么
更新的是 ans+=Max[p]-max(Max[fail[i]]+1,k)+1)就好,然后更新父节点,首先匹配最长公共子串的时候能到这个点证明长度必然 >=Min[p] 那么父节点必然也可以更新,比较k和Min[p] 如果k大于等于Min[p],那么父节点必然没贡献,不然就是有贡献 标记一下,最后拓扑更新就行

#include <bits/stdc++.h>using namespace std;const int maxn = 200000+5;int last,tail,in[maxn],Min[maxn];int Max[maxn],cnt[maxn],vis[maxn];int nxt[maxn][26],fail[maxn];char sa[maxn],sb[maxn];typedef long long ll;ll sz[maxn];int Maxi[maxn];inline void build(char *s){    while(*s)    {        int p=last,t=++tail,c=*s++-'a';        Max[t]=Max[p]+1;        sz[t]=1;        Maxi[t]=0;        while(p&&!nxt[p][c])            nxt[p][c]=t,p=fail[p];        if(p)        {            int q=nxt[p][c];            if(Max[q]==Max[p]+1)                fail[t]=q,Min[t]=Max[q]+1;            else            {                int k=++tail;                Maxi[k]=0;                fail[k]=fail[q];                fail[t]=fail[q]=k;                Max[k]=Max[p]+1;                memcpy(nxt[k],nxt[q],sizeof(nxt[q]));                while(p&&nxt[p][c]==q)                    nxt[p][c]=k,p=fail[p];            }        }        else            fail[t]=Min[t]=1;        last=t;    }}int b[maxn];ll flag[maxn];int main(){    int k;    while(~scanf("%d",&k))    {        if(!k) break;        int n;        memset(nxt,0,sizeof(nxt));        memset(b,0,sizeof(b));        memset(cnt,0,sizeof(cnt));        memset(flag,0,sizeof(flag));        scanf(" %s",sa);        last=1,tail=1;        build(sa);        int hh=strlen(sa);        for(int i=1;i<=tail;i++) cnt[Max[i]]++;        for(int i=1;i<=hh;i++) cnt[i]+=cnt[i-1];        for(int i=1;i<=tail;i++) b[cnt[Max[i]]--]=i;        for(int i=tail;i>=1;i--) sz[fail[b[i]]]+=sz[b[i]];        ll ans=0;        scanf(" %s",sb);        n=strlen(sb);        int p=1;        int len=0;        for(int i=0;i<n;i++)        {            int c=sb[i]-'a';            if(nxt[p][c]) len++,p=nxt[p][c];            else            {                while(p&&!nxt[p][c])                    p=fail[p];                if(!p) p=1,len=0;                else {                    len=Max[p]+1;                    p=nxt[p][c];                }            }//            Maxi[p]=max(Maxi[p],len);//            if(len>=k) flag[p]++;              if(len>=k)              {                  ans+=1ll*(len-max(k,Max[fail[p]]+1)+1)*sz[p];                  if(k<=Max[fail[p]]) flag[fail[p]]++;              }        }        for(int i=tail;i>=1;i--)        {            int p=b[i];            ans+=max(0ll,1ll*flag[p]*(Max[p]-max(Max[fail[p]]+1,k)+1)*sz[p]);            if(k<=Max[fail[p]])                    flag[fail[p]]+=flag[p];        }        printf("%lld\n",ans );    }}
阅读全文
0 0
原创粉丝点击