【COCI2013】slasticar

14 篇文章 0 订阅
6 篇文章 0 订阅

题目大意

给出一个长度为n的字符串A,接下来给出m个字符串,对于每个字符串B,用给出的方法去与A匹配:
1. 设B的长度为L,先与A的位置为1…L的一段进行匹配:先比较A[1]和B[1],接下来比较A[2]和B[2],直到比较完A[L]和B[L]全部匹配或出现一个不相同(即A[i]≠B[i])
2. 如果匹配失败,则匹配A的2..L+1和B的1..L,如果得不到长度为L的串(如:A的长度为12,L=6,则A[8..13]与B[1..6]匹配,但是13>12),则在A后面补’#’直到长度为L(即变成‘XXXX##’)。以此类推
3. 如果匹配成功或A[len(A)..len(A)+L-1]也匹配完,则退出匹配
求每个字符串匹配过程比较次数

样例:
【input】
7
1090901
4
87650
0901
109
090

【output】
7
10
3
4

解释:第一个字符串’87650’与A每一个片段都只比较了第1位就失败了;
第二个’0901’,先与’1090’比较1次,然后与’0909’比较4次(因为’090’是它们的公共前缀,则前3位都相同,但比较到第4位时’1’不等于’9’),然后与’9090’只比较了第1位,接着与’0901’匹配成功,比较了4次,总共1+4+1+4=10次

数据范围:n≤ 105 m≤ 5104 单个字符串B长度不超过 105 所有字符串B总长不超过 3106 所有字符串均由数字0…9组成

限制:Time Limits:3s Memory Limits:256MB

正解

先考虑当前字符串B 不能匹配成功 的情况:
对于A[i..i+L-1],设它与B的LCP为lcp(i),则答案 Ans= ni=1lcp(i)+1
那么我们可以枚举这个lcp,然后确定有多少段A[i..i+L-1]与B的LCP等于这个值
显然,把A[i..i+L-1]看成A的后缀Suffix(i)不会影响答案,因为现在考虑的是不能匹配成功的情况。
所以可以先给A的后缀排个序,然后从小到大枚举这个lcp,设它是j,就可以二分出一个区间l..r,使 lcp(i)j[lir]
这样时间复杂度就只有 O(Llogn)

再考虑字符串B 能匹配成功 的情况:
同样通过二分,可以确定lcp≥L的区间,在这个区间里找一个出现位置最前的(即sa的最小值),设它为p,则答案Ans= pi=1lcp(i)+1
当我们枚举lcp时,确定了一个区间l..r,那么只有sa[i]≤p [l≤i≤r]的才能统计入答案,打个主席树就好了
时间复杂度还是 O(Llogn)

#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;

const int maxn=100005,maxm=262205,Maxm=2000005;

typedef long long LL;

int n,m,M,sa[maxn],height[maxn],rank[maxn],xx[maxn],yy[maxn],s[maxn],v[maxn],le[maxn],ri[maxn],len,t[maxm];

LL ans;

char S[maxn],Str[maxn];

struct Chairman_Tree
{
    int tot,l[Maxm],r[Maxm],sum[Maxm],root[maxm];
    void add(int L,int R,int v,int &x,int y)
    {
        x=++tot;
        sum[x]=sum[y]+1;
        if (L==R) return;
        int mid=(L+R)/2;
        l[x]=l[y];r[x]=r[y];
        if (v<=mid) add(L,mid,v,l[x],l[y]);else add(mid+1,R,v,r[x],r[y]);
    }
    int count(int L,int R,int v,int x,int y)
    {
        if (v>=R) return sum[y]-sum[x];
        int mid=(L+R)/2;
        if (v<=mid) return count(L,mid,v,l[x],l[y]);
        return count(mid+1,R,v,r[x],r[y])+sum[l[y]]-sum[l[x]];
    }
}T;

void add(int l,int r,int g,int v,int x)
{
    t[x]=min(t[x],v);
    if (l==r) return;
    int mid=(l+r)/2;
    if (g<=mid) add(l,mid,g,v,x*2);else add(mid+1,r,g,v,x*2+1);
}

int getmin(int l,int r,int a,int b,int x)
{
    if (l==a && r==b) return t[x];
    int mid=(l+r)/2;
    if (b<=mid) return getmin(l,mid,a,b,x*2);
    if (a>mid) return getmin(mid+1,r,a,b,x*2+1);
    return min(getmin(l,mid,a,mid,x*2),getmin(mid+1,r,mid+1,b,x*2+1));
}

bool cmp(int *r,int a,int b,int l)
{
    return r[a]==r[b] && r[a+l]==r[b+l];
}

void build_sa()
{
    memset(xx,255,sizeof(xx)); memset(yy,255,sizeof(yy));
    int i,p,l,m='9'+1,*x=xx,*y=yy,*t;
    for (i=0;i<n;i++) s[x[i]=S[i]]++;
    for (i=1;i<m;i++) s[i]+=s[i-1];
    for (i=n-1;i>=0;i--) sa[--s[x[i]]]=i;
    for (l=p=1;p<n;m=p,l*=2)
    {
        for (p=0,i=n-l;i<n;i++) y[p++]=i;
        for (i=0;i<n;i++) if (sa[i]>=l) y[p++]=sa[i]-l;
        for (i=0;i<m;i++) s[i]=0;
        for (i=0;i<n;i++) s[v[i]=x[y[i]]]++;
        for (i=1;i<m;i++) s[i]+=s[i-1];
        for (i=n-1;i>=0;i--) sa[--s[v[i]]]=y[i];
        for (t=x,x=y,y=t,x[sa[0]]=0,i=p=1;i<n;i++)
            x[sa[i]]=cmp(y,sa[i-1],sa[i],l)?p-1:p++;
    }
}

void build_height()
{
    int i,j,k=0;
    for (i=0;i<n;i++) rank[sa[i]]=i;
    for (i=0;i<n;height[rank[i++]]=k)
    {
        k=k?k-1:k;
        if (rank[i])
        {
            for (j=sa[rank[i]-1];S[j+k]==S[i+k];k++);
        }
    }
}

void init_data_structures()
{
    memset(t,127,sizeof(t));
    for (int i=0;i<n;i++)
    {
        T.add(0,n-1,sa[i],T.root[i+1],T.root[i]);
        add(0,n-1,i,sa[i],1);
    }
}

void init()
{
    scanf("%d",&n);
    scanf("%s",&S);
    build_sa();
    build_height();
    init_data_structures();
}

int find(int l,int r,char c,int x)
{
    if (l>r) return l;
    for (int mid=(l+r)/2;l<r;mid=(l+r)/2)
        if (sa[mid]+x>=n || S[sa[mid]+x]<c) l=mid+1;else r=mid;
    if (sa[l]+x>=n || S[sa[l]+x]<c) return l+1;else return l;
}

void work()
{
    scanf("%d",&m);
    while (m--)
    {
        scanf("%s",Str);
        len=strlen(Str);
        le[0]=0;ri[0]=n-1;
        for (M=1;M<=len;M++)
        {
            le[M]=find(le[M-1],ri[M-1],Str[M-1],M-1); ri[M]=find(le[M],ri[M-1],Str[M-1]+1,M-1)-1;
            if (le[M]>ri[M]) break;
        }
        ans=0;
        if (M<=len)
        {
            for (int i=0;i<M;i++) ans+=ri[i]-le[i]+1;
            printf("%lld\n",ans);
        }else
        {
            int last=getmin(0,n-1,le[len],ri[len],1);
            for (int i=0;i<len;i++) ans+=T.count(0,n-1,last,T.root[le[i]],T.root[ri[i]+1]);
            printf("%lld\n",ans);
        }
    }
}

int main()
{
    freopen("slasticar.in","r",stdin); freopen("slasticar.out","w",stdout);
    init();
    work();
    fclose(stdin); fclose(stdout);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值