bzoj2384

树状数组+KMP

匹配问题上KMP

但是问题在于如何判断两个位置相等,我们认为如果一个位置之前比他小的数数量相同那么就是相等。

那么我们用树状数组动态维护这个东西,每次跳nxt的时候用树状数组删除数。因为每个数只加入一次,所以复杂度是nlogn的,为什么这样是对的呢?我们这么想,对于当前加入最后的一个字符,这个肯定是对的,如果我们再加入一个数,如果比这个数小,那么影响,否则不影响,现在两个串同时加入两个数,那么如果一个相对大一个相对小,那么这个位置肯定是不匹配的,所以即使影响了之前也没关系。大概是这样吧

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 1e6 + 5;
int n, m;
int a[N], tree[N], s[N], v[N], b[N], c[N], nxt[N], ans[N];
void update(int x, int d)
{
    for(; x <= m; x += x & -x) tree[x] += d;
}
int query(int x)
{
    int ret = 0;
    for(; x; x -= x & -x) ret += tree[x];
    return ret;
}
int main()
{
    scanf("%d%d", &n, &m);
    for(int i = 1; i <= n; ++i) scanf("%d", &a[i]), s[a[i]] = i;
    for(int i = 1; i <= n; ++i) v[i] = query(s[i]), update(s[i], 1);
    for(int i = 1; i <= m; ++i) scanf("%d", &b[i]), c[i] = b[i];
    memset(tree, 0, sizeof(tree));
    for(int i = 2, j = 0; i <= n; ++i)
    {
        while(query(s[i]) != v[j + 1]) 
        {
            for(int k = i - j; k < i - nxt[j]; ++k) update(s[k], -1);
            j = nxt[j];
        }
        if(query(s[i]) == v[j + 1])
        {
            update(s[i], 1);
            ++j;
        }
        nxt[i] = j;
    }
    sort(c + 1, c + m + 1);
    memset(tree, 0, sizeof(tree));
    for(int i = 1, j = 0; i <= m; ++i)
    {
        b[i] = lower_bound(c + 1, c + m + 1, b[i]) - c;
        while(j == n || query(b[i]) != v[j + 1])
        {
            for(int k = i - j; k < i - nxt[j]; ++k) update(b[k], -1);
            j = nxt[j];
        }
        if(query(b[i]) == v[j + 1])
        {
            ++j;
            update(b[i], 1);
        }
        if(j == n) ans[++ans[0]] = i - j + 1;
    }
    printf("%d
", ans[0]);
    for(int i = 1; i <= ans[0]; ++i) printf("%d%c", ans[i], i == ans[0] ? '
' : ' ');
    return 0;
}
View Code
原文地址:https://www.cnblogs.com/19992147orz/p/7868097.html