【COCI2013】slasticar

Description

这里写图片描述

Input这里写图片描述

Output

这里写图片描述

Sample Input

输入1:

7
1090901
4
87650
0901
109
090

输入2:

10
5821052680
4
210526
2105
582
105268

输入3:

3
001
1
11

Sample Output
输出1:

7
10
3
4

输出2:

8
6
3
9

输出3:

4

Data Constraint

20%的数据满足1<=N<=1000,1<=M<=500,单独一个添加剂序号的长度不超过1000

Hint

这里写图片描述


题目大意:

给两个字符串A,B,用B和A的每一个前缀一个个字符匹配,如果成功,则退出,否则一直匹配到结尾,对于当前匹配,如果当前字符不一样,就退出进入下一个前缀匹配,求匹配次数。

如果只求在哪一个位置匹配成功,那么是AC自动机的裸题,可惜并不是。

首先考虑不能匹配成功的,将A的后缀排序,对于B的每一个前缀,在排好序的A的后缀中二分出一个区间,使得这个区间的后缀全部能与B的这个前缀匹配,答案就是区间的长度和再加上n,因为还要加上没有匹配成功的那一次。

如果能匹配成功,匹配成功的位置是整个B串在A的后缀中二分出的区间内的后缀位置的最小值,设二分出的区间是l..r,匹配成功的位置就是最小的SA[l..r],这个可以通过RMQ解决。

我们得到了匹配成功的位置w,那么现在就是要求SA[l_i..r_i] <= w的个数,主席树可以完美处理这个问题。

#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define fo(i,x,y) for(int i=x;i<=y;i++)
#define fd(i,x,y) for(int i=x;i>=y;i--)
#define min(a, b) ((a) < (b) ? (a) : (b))

using namespace std;

const int Maxn = 100005;
char str[Maxn];
int T,n,m,len,ans,a[Maxn],b[Maxn];
int SA[Maxn],Height[Maxn],tax[Maxn],tp[Maxn],rank[Maxn];
int a2[17],fj[17][Maxn];
int g[Maxn],tot;
struct node {
    int l, r, s;
}d[Maxn * 40];
int l[Maxn], r[Maxn];

void Init() {
    scanf("%d\n", &n);
    scanf("%s", str);
    fo(i, 1, n) a[i] = str[i - 1];  
}

bool cmp(int *f,int x,int y,int w) {return f[x] == f[y] && f[x + w] == f[y + w];}
void Rsort() {
    fo(i, 1, m) tax[i] = 0;
    fo(i, 1, n) tax[rank[tp[i]]] ++;
    fo(i, 1, m) tax[i] += tax[i - 1];
    fd(i, n, 1) SA[tax[rank[tp[i]]] --] = tp[i];
}
void Suffix() {
    fo(i, 1, n) rank[i] = a[i], tp[i] = i;
    m = 127, Rsort();
    for(int w = 1, p = 1, i; p < n; w += w, m = p) {
        for(p = 0, i = n - w + 1; i <= n; i ++) tp[++ p] = i;
        fo(i, 1, n) if(SA[i] > w) tp[++ p] = SA[i] - w;
        Rsort(), swap(rank, tp), rank[SA[1]] = p = 1;
        fo(i, 2, n) rank[SA[i]] = cmp(tp, SA[i - 1], SA[i], w) ? p : ++ p;
    }
    int j, k = 0;
    for(int i = 1; i <= n;Height[rank[i ++]] = k)
        for( k = k ? k - 1 : k, j = SA[rank[i] - 1]; a[i + k] == a[j + k]; ++ k);
}

void build_multiplication() {
    a2[0] = 1; fo(i, 1, 16) a2[i] = a2[i - 1] << 1;
    fo(i, 1, n) fj[0][i] = SA[i];
    fo(j, 1, 16) fo(i, 1, n) fj[j][i] = min(fj[j - 1][i], fj[j - 1][i + a2[j - 1]]);
}
int find_min(int x, int y) {
    int lg = log2(y - x +1);
    return min(fj[lg][x], fj[lg][y - a2[lg] + 1]);
}

void inc(int i, int x, int y, int w) {
    if(x == y) d[i].s ++; else {
        int m = (x + y) / 2;
        if(w <= m) {
            d[++ tot] = d[d[i].l], d[i].l = tot;
            inc(d[i].l, x, m, w);
        } else {
            d[++ tot] = d[d[i].r], d[i].r = tot;
            inc(d[i].r, m + 1, y, w);
        }
        d[i].s = d[d[i].l].s + d[d[i].r].s;
    }
}
void build_time() {
    g[0] = tot = 1;
    fo(i, 1, n) {
        d[++ tot] = d[g[i - 1]]; g[i] = tot;
        inc(g[i], 1, n, rank[i]);
    }
}
int find_sum(int i, int x, int y, int l, int r) {
    if(i == 0) return 0;
    if(l > r) return 0;
    if(x == l && y == r) return d[i].s;
    int m = (x + y) / 2;
    if(r <= m) return find_sum(d[i].l, x, m, l, r);
    if(l > m) return find_sum(d[i].r, m + 1, y, l, r);
    return find_sum(d[i].l, x, m, l, m) + find_sum(d[i].r, m + 1, y, m + 1, r);
}

int cp(int st,int len) {
    int i = st, j = 1;
    for(;i <= n && j <= len; i ++, j ++) {
        if(a[i] > b[j]) return 2;
        if(a[i] < b[j]) return 0;
    }
    if(j > len) return 1;
    return 0;
}
int erf(int x,int y,int z,int ex) {
    int ans = z ? 0 : len + 1;
    for(int l = x, r = y; l <= r;) {
        int m = (l + r) / 2;
        int bz = cp(SA[m], ex);
        if(bz == 1) {
            ans = m;
            if(z) l = m + 1; else r = m - 1;
        }
        if(bz == 0) l = m + 1;
        if(bz == 2) r = m - 1;
    }
    return ans;
}
void solution() {
    scanf("%d", &T);
    fo(cc, 1, T) {
        ans = 0;
        scanf("%s", str);
        len = strlen(str);
        fo(i, 0, len - 1) b[i + 1] = str[i];
        l[0] = 1, r[0] = n;
        fo(now, 1, len) {
            l[now] = erf(l[now - 1], r[now - 1], 0, now);
            r[now] = erf(l[now - 1], r[now - 1], 1, now);
        }
        int mm = l[len] <= r[len] ? find_min(l[len], r[len]) : n;
        fo(now, 1, len) if(l[now] <= r[now]) {
            ans += find_sum(g[mm], 1, n, l[now], r[now]);
        }
        ans += mm;
        ans -= r[len] >= l[len];
        printf("%d\n", ans);
    }
}

int main() {
    freopen("slasticar.in","r",stdin);
    freopen("slasticar.out","w",stdout);
    Init();
    Suffix();
    build_multiplication();
    build_time();
    solution();
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值