zoukankan      html  css  js  c++  java
  • 洛谷P3763

    Portal

    Description

    给出字符串(s,s_0(|s|,|s_0|leq10^5)),求有多少个(s_0)的连续子串修改小于等于三个字母能够变成(s)。共有(T(Tleq10))组测试数据。

    Solution

    后缀数组。
    易知(s_0)(|s_0|-|s|+1)个长度为(|s|)的子串,我们依次检查这些子串是否合法。
    检查从位置(i)开始的子串是否合法时,设已经匹配了(j)位,那么求出(s_0)(i+j)位与(s)(j+1)位的最长公共前缀(len),说明他们匹配(len)位后失配。那么用掉一次修改机会跳过这一位,即(j=j+len),继续进行匹配。如果三次机会都用掉了依然有(j<|s|),说明不合法,否则合法。求LCP可以将(s_0)(s)拼起来再求后缀数组来(O(1))得到。

    时间复杂度(O(Tcdot|s|log|s|))

    Code

    //[TJOI2017]DNA
    #include <algorithm>
    #include <cstdio>
    #include <cstring>
    using std::min; using std::swap;
    const int N=2e5+10;
    int n,m; char s0[N],s[N];
    int sa[N],rnk[N<<1],h[N];
    int cnt[N],tmp[N],rnk1[N];
    int Lg2[N],rmq[N][20];
    void getSA(char s[],int n)
    {
        memset(cnt,0,sizeof cnt);
        for(int i=1;i<=n;i++) cnt[s[i]]=1;
        for(int i=1;i<=256;i++) cnt[i]+=cnt[i-1];
        for(int i=1;i<=n;i++) rnk[i]=cnt[s[i]];
        for(int L=1;L<=n;L<<=1)
        {
            memset(cnt,0,sizeof cnt);
            for(int i=1;i<=n;i++) cnt[rnk[i+L]]++;
            for(int i=1;i<=n;i++) cnt[i]+=cnt[i-1];
            for(int i=n;i>=1;i--) tmp[cnt[rnk[i+L]]--]=i;
            memset(cnt,0,sizeof cnt);
            for(int i=1;i<=n;i++) cnt[rnk[tmp[i]]]++;
            for(int i=1;i<=n;i++) cnt[i]+=cnt[i-1];
            for(int i=n;i>=1;i--) sa[cnt[rnk[tmp[i]]]--]=tmp[i];
            int k=0;
            for(int i=1;i<=n;i++)
            {
                if(rnk[sa[i]]!=rnk[sa[i-1]]||rnk[sa[i]+L]!=rnk[sa[i-1]+L]) k++;
                rnk1[sa[i]]=k;
            }
            memcpy(rnk,rnk1,sizeof rnk1);
            if(k>=n) break;
        }
        for(int i=1,k=0;i<=n;i++)
        {
            if(rnk[i]==1) {h[1]=k=0; continue;}
            if(k>0) k--;
            while(s[i+k]==s[sa[rnk[i]-1]+k]) k++;
            h[rnk[i]]=k;
        }
        for(int i=1;i<=n;i++) rmq[i][0]=h[i];
        for(int i=2;i<=n;i++) Lg2[i]=Lg2[i>>1]+1;
        for(int k=1;(1<<k)<=n;k++)
            for(int i=1;i+(1<<k)-1<=n;i++) rmq[i][k]=min(rmq[i][k-1],rmq[i+(1<<k-1)][k-1]);
    }
    int lcp(int x,int y)
    {
        int i=rnk[x],j=rnk[y];
        if(i>j) swap(i,j);
        int t=Lg2[j-i];
        return min(rmq[i+1][t],rmq[j-(1<<t)+1][t]);
    }
    int main()
    {
        int task; scanf("%d",&task);
        while(task--)
        {
        
        scanf("%s%s",s0+1,s+1);
        n=strlen(s0+1),m=strlen(s+1);
        s0[n+1]='#';
        for(int i=1;i<=m;i++) s0[n+1+i]=s[i];
        getSA(s0,n+m+1);
        int ans=0;
        for(int i=1;i<=n-m+1;i++)
        {
            int j=0;
            for(int k=0;j<=m&&k<=3;j++,k++) j+=lcp(i+j,n+2+j);
            if(j>m) ans++;
        }
        printf("%d
    ",ans);
    
        }
        return 0;
    }
    

    P.S.

    现在一想好简单啊...
    因为要拼起来所以后缀数组大小为2e5。

  • 相关阅读:
    log4j输出信息到mongodb
    mongodb日志服务器方案
    mongodb的高级操作(聚合框架)
    mongdb高级操作(group by )
    mongodb的优化
    mongodb集成spring
    mongodb的固定集合(优化效率)
    mongodb的查询操作符
    mongoDB中的连接池(转载)
    mongodb在java驱动包下的操作(转)
  • 原文地址:https://www.cnblogs.com/VisJiao/p/LgP3763.html
Copyright © 2011-2022 走看看