POJ-3415 (后缀数组+单调栈/并查集)

题目链接:传送门

大致题意:现给定字符串s和t ,求s和t的长度不小于k的公共子串个数;

题目思路:

  对于s的每一个后缀和t的每一个后缀求lcp,如果匹配出的lcp=x,那么ans+=x-k+1(x>=k),直接暴力显然不行,就有了下面的方法:

  将两个串做连接得到字符串str,中间隔一个绝对不会出现的任意字符,比如‘#’,得到str = s + '#' + t 。

  再对str求sa和height(加‘#’的目的是为了让height数组的值不超过s和t的长度。例如s=‘aaaaa’,t=‘aaaa’,直接连接的话,显然会使height的值过大,根据height[i]的定义是排名i和i-1的最长公共前缀)

  lcp(i,j) = min ( height[i+1] , height[i+2] , ... , height[j] ) ;

  对于排名为j且属于t串的后缀,直接求所有的lcp(i,j) 之和,其中i∈s且 i < j ,显然lcp(i,j) 与 lcp(i,j+1) 是有联系的(多了一个数字取min,lcp(i,j) <= lcp(i,j+1) ),因此就没必要重复计算

  那么根据排名从小到大进行扫描,并且根据其height值维护一个单调栈(单增栈),单调栈的作用是维护[1,i-1] 中的s对 i 的贡献 

  

  另一种思路,可以直接用并查集来算,也是可行的;

 

#include<cstdio>
#include<cstring>
#include<ctype.h>
#include<algorithm>
#include<functional>
#pragma GCC optimize(2)
using namespace std;
//std::mt19937 rnd(233);
typedef long long LL;
typedef pair<int,int> pii;
typedef pair<LL,LL> pLL;
#define pb push_back
#define mk make_pair
#define fi first
#define se second
#define ls (i<<1)
#define rs (i<<1|1)
#define mem(a,b) memset(a,b,sizeof(a))
const int N=1e6+5;
const int inf=0x3f3f3f3f;
const LL mod=1e9+7;
LL read()
{
    LL x=0,f=1;
    char ch=getchar();
    while(!isdigit(ch)){ if(ch=='-') f=-1; ch=getchar(); }
    while(isdigit(ch)){ x=10*x+ch-'0'; ch=getchar(); }
    return f*x;
}
int sa[N],rk[N],x[N],y[N],he[N],c[N],n,m,sta[N],num[N];
char s[N],s1[N];
void SA()
{
    int m=130;
    for(int i=1;i<=m;i++) c[i]=0;
    for(int i=1;i<=n;i++) c[x[i]=s[i]]++;
    for(int i=1;i<=m;i++) c[i]+=c[i-1];
    for(int i=n;i;i--) sa[c[x[i]]--]=i;
    for(int k=1;k<=n;k<<=1)
    {
        int tot=0;
        for(int i=1;i<=m;i++) c[i]=0;
        for(int i=n-k+1;i<=n;i++) y[++tot]=i;
        for(int i=1;i<=n;i++) if(sa[i]>k) y[++tot]=sa[i]-k;
        for(int i=1;i<=n;i++) c[x[i]]++;
        for(int i=1;i<=m;i++) c[i]+=c[i-1];
        for(int i=n;i;i--) sa[c[x[y[i]]]--]=y[i];
        swap(x,y);
        tot=x[sa[1]]=1;
        for(int i=2;i<=n;i++)
            x[sa[i]]=(y[sa[i]]==y[sa[i-1]]&&y[sa[i]+k]==y[sa[i-1]+k])?tot:++tot;
        if(tot==n) break;
        m=tot;
    }
}
void gethe()
{
    he[1]=0;
    for(int i=1;i<=n;i++) rk[sa[i]]=i;
    for(int i=1,k=0;i<=n;i++)
    {
        if(rk[i]==1) continue;
        if(k) k--;
        int j=sa[rk[i]-1];
        while(i+k<=n&&j+k<=n&&s[i+k]==s[j+k]) k++;
        he[rk[i]]=k;
    }
}
int main()
{
    int k;
    while(scanf("%d",&k)!=EOF&&k)
    {
        scanf("%s",s+1);
        scanf("%s",s1+1);
        int l1=strlen(s+1),l2=strlen(s1+1);
        n=l1+l2+1;
        s[l1+1]='#';
        for(int i=1;i<=l2;i++) s[l1+1+i]=s1[i];
        SA(); gethe();
        //printf("s = %s\n",s+1);
        //for(int i=1;i<=n;i++) printf("%d%c",sa[i],i==n?'\n':' ');
        //for(int i=1;i<=n;i++) printf("%d%c",he[i],i==n?'\n':' ');
        int top=0;
        LL res=0,ans=0;
        for(int i=1;i<=n;i++)//对于每一个i算出[1,i-1]对i点的贡献res, 这一次的res由上一次的res转移得到
        {
            if(he[i]<k) top=res=0;
            else
            {
                num[i]=0;
                if(sa[i-1]<=l1) num[i]++,res+=he[i]-k+1;
                while(top>0&&he[sta[top]]>=he[i])
                    num[i]+=num[sta[top]],
                    res-=num[sta[top]]*(he[sta[top]]-he[i]),top--;//加入了一个更小的,由于之前是直接加上的he[i]-k+1,根据lcp=min(...),对于i就减去之前多加的值
                sta[++top]=i;//可以理解为把凸起来的山包给削平了
                if(sa[i]>l1+1) ans+=res;
            }
        }
        for(int i=1;i<=n;i++)//对于每一个i算出[1,i-1]对i点的贡献res
        {
            if(he[i]<k) top=res=0;
            else
            {
                num[i]=0;
                if(sa[i-1]>l1+1) num[i]++,res+=he[i]-k+1;
                while(top>0&&he[sta[top]]>=he[i])
                    num[i]+=num[sta[top]],
                    res-=num[sta[top]]*(he[sta[top]]-he[i]),top--;
                sta[++top]=i;
                if(sa[i]<=l1) ans+=res;
            }
        }
        printf("%lld\n",ans);
        for(int i=1;i<=n;i++) x[i]=y[i]=0;
    }
    return 0;
}
单调栈

 

  1 #include<cstdio>
  2 #include<cstring>
  3 #include<ctype.h>
  4 #include<functional>
  5 #include<algorithm>
  6 #pragma GCC optimize(2)
  7 using namespace std;
  8 typedef long long LL;
  9 typedef pair<int,int> pii;
 10 typedef pair<LL,LL> pLL;
 11 #define pb push_back
 12 #define mk make_pair
 13 #define fi first
 14 #define se second
 15 #define ls (i<<1)
 16 #define rs (i<<1|1)
 17 #define mem(a,b) memset(a,b,sizeof(a))
 18 const int N=1e6+5;
 19 const int inf=0x3f3f3f3f;
 20 const LL mod=1e9+7;
 21 LL read()
 22 {
 23     LL x=0,f=1;
 24     char ch=getchar();
 25     while(!isdigit(ch)){ if(ch=='-') f=-1; ch=getchar(); }
 26     while(isdigit(ch)){ x=10*x+ch-'0'; ch=getchar(); }
 27     return f*x;
 28 }
 29 pii a[N];
 30 int sa[N],rk[N],he[N],x[N],y[N],c[N],n,m,f[N],cs[N],ct[N];
 31 char s[N],t[N];
 32 void SA()
 33 {
 34     m=128;
 35     for(int i=1;i<=m;i++) c[i]=0;
 36     for(int i=1;i<=n;i++) c[x[i]=s[i]]++;
 37     for(int i=1;i<=m;i++) c[i]+=c[i-1];
 38     for(int i=n;i;i--) sa[c[x[i]]--]=i;
 39     for(int k=1;k<=n;k<<=1)
 40     {
 41         int tot=0;
 42         for(int i=n-k+1;i<=n;i++) y[++tot]=i;
 43         for(int i=1;i<=n;i++) if(sa[i]>k) y[++tot]=sa[i]-k;//把sa当做第二关键字的排名(第一关键字和第二关键字等长)
 44         for(int i=1;i<=m;i++) c[i]=0;
 45         for(int i=1;i<=n;i++) c[x[i]]++;
 46         for(int i=1;i<=m;i++) c[i]+=c[i-1];
 47         for(int i=n;i;i--) sa[c[x[y[i]]]--]=y[i];
 48         swap(x,y);
 49         x[sa[1]]=tot=1;
 50         for(int i=2;i<=n;i++)
 51             x[sa[i]]=y[sa[i]]==y[sa[i-1]]&&y[sa[i]+k]==y[sa[i-1]+k]?tot:++tot;
 52         if(tot==n) break;
 53         m=tot;
 54     }
 55 
 56 }
 57 void gethe()
 58 {
 59     he[1]=0;
 60     for(int i=1;i<=n;i++) rk[sa[i]]=i;
 61     for(int i=1,k=0;i<=n;i++)
 62     {
 63         if(rk[i]==1) continue;
 64         int j=sa[rk[i]-1];
 65         if(k) k--;
 66         while(i+k<=n&&j+k<=n&&s[i+k]==s[j+k]) k++;
 67         he[rk[i]]=k;
 68     }
 69 }
 70 int getf(int x)
 71 {
 72     return x==f[x]?f[x]:f[x]=getf(f[x]);
 73 }
 74 int main()
 75 {
 76     int k;
 77     while(scanf("%d",&k)!=EOF&&k)
 78     {
 79         scanf("%s%s",s+1,t+1);
 80         int l1=strlen(s+1),l2=strlen(t+1);
 81         s[l1+1]='#';
 82         n=l1+l2+1;
 83         for(int i=1;i<=l2;i++) s[i+l1+1]=t[i];
 84         SA(); gethe();
 85         for(int i=1;i<=n;i++)
 86         {
 87             f[i]=i;
 88             cs[i]=(i<=l1);
 89             ct[i]=(i>l1+1);
 90         }
 91         for(int i=1;i<=n;i++) a[i]=mk(he[i],i);
 92         sort(a+1,a+n+1);
 93         LL ans=0;
 94        // for(int i=1;i<=n;i++) printf("%d%c",sa[i],i==n?'\n':' ');
 95         for(int i=n;i;i--)
 96         {
 97             //printf("%d %d\n",a[i].fi,a[i].se);
 98             if(a[i].fi<k) break;
 99             int id=a[i].se;
100             //if(rk[id]==1) continue;
101             int fx=getf(sa[id]),fy=getf(sa[id-1]);
102             if(fx==fy) continue;
103             f[fy]=fx;
104             ans+=1LL*(cs[fy]*ct[fx]+cs[fx]*ct[fy])*(a[i].fi-k+1);
105             cs[fx]+=cs[fy];
106             ct[fx]+=ct[fy];
107         }
108         printf("%lld\n",ans);
109         for(int i=1;i<=n;i++) x[i]=y[i]=0;
110     }
111     return 0;
112 }
并查集

 

posted @ 2020-10-09 22:01  DeepJay  阅读(116)  评论(0编辑  收藏  举报