JZOJ 3638. 【COCI2013】slasticar

题目大意

给出一个长度为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次

数据范围:

n105
m5104
单个字符串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 <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <cmath>

#define N 100005
#define M 262205

#define ll long long

using namespace std;

int n,m,len;
ll ans;

struct TREE
{
    int tot;
    int l[M * 10], r[M * 10], sum[M * 10], root[M * 10];

    void add(int L,int R,int v,int &cnt,int last)
    {
        cnt = ++tot;
        sum[cnt] = sum[last] + 1;
        if (L == R)
            return;
        int mid = (L + R) / 2;
        l[cnt] = l[last];
        r[cnt] = r[last];
        if (v <= mid)
            add(L,mid,v,l[cnt],l[last]);
            else add(mid + 1,R,v,r[cnt],r[last]);
    }

    int count(int L,int R,int v,int cnt,int last)
    {
        if (v >= R)
            return sum[last] - sum[cnt];
        int mid = (L + R) / 2;
        if (v <= mid)
            return count(L,mid,v,l[cnt],l[last]);
            else return count(mid + 1,R,v,r[cnt],r[last]) + sum[l[last]] - sum[l[cnt]];
    }
}tree;

int t[M];

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));
}

int s[N],v[N];
int wa[N],wb[N];
int le[N],ri[N];
int sa[N],height[N];

char ch[N],Str[N];

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

void getSA()
{
    memset(wa,255,sizeof(wa));
    memset(wb,255,sizeof(wb));
    int i,p,l,Max = '9' + 1;
    int *x = wa;
    int *y = wb;
    int *t;

    for (i = 0; i < n; i++)
        s[x[i] = ch[i]]++;
    for (i = 1; i < Max; i++)
        s[i] += s[i - 1];
    for (i = n - 1; i >= 0; i--)
        sa[--s[x[i]]] = i;
    for (l = p = 1; p < n; Max = 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 < Max; i++)
            s[i] = 0;
        for (i = 0; i < n; i++)
            s[v[i] = x[y[i]]]++;
        for (i = 1; i < Max; 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++;
    }
}

int rank[N];

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

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

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

int main()
{
    freopen("slasticar.in","r",stdin);
    freopen("slasticar.out","w",stdout);
    scanf("%d",&n);
    scanf("%s",ch);

    getSA();
    getHeight();
    getTree();

    int T;
    scanf("%d",&T);
    while (T--)
    {
        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 += tree.count(0,n - 1,last,tree.root[le[i]],tree.root[ri[i] + 1]);
            printf("%lld\n",ans);
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值