zoukankan      html  css  js  c++  java
  • 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
    ",s+1);
            //for(int i=1;i<=n;i++) printf("%d%c",sa[i],i==n?'
    ':' ');
            //for(int i=1;i<=n;i++) printf("%d%c",he[i],i==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
    ",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?'
    ':' ');
     95         for(int i=n;i;i--)
     96         {
     97             //printf("%d %d
    ",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
    ",ans);
    109         for(int i=1;i<=n;i++) x[i]=y[i]=0;
    110     }
    111     return 0;
    112 }
    并查集
  • 相关阅读:
    linux一周学习总结
    浅谈用户、组之管理指令
    关于基因组注释文件GTF的解释
    swiper display:none 后 在显示 滑动问题
    CSS相关
    常用正则表达式
    CSS Flex布局
    原生js去掉所有的html标签,最终得到HTML标签中的所有内容
    python 爬虫与数据可视化--python基础知识
    mongodb的安装与配置
  • 原文地址:https://www.cnblogs.com/DeepJay/p/13789091.html
Copyright © 2011-2022 走看看