[CEOI2011]Matching

壹、题目描述 ¶

传送门 to LOJ .

贰、题解 ¶

我他妈居然没有想到使用反序表进行比较,真的是脑抽了......

我们可以通过 \(a_i\) 得到某个区间离散化之后应该长得样子,我们记这个样子为 \(p\),那么,我们现在考察的就是 \(p\)\(b\) 中任意长度为 \(n\) 的区间离散化之后的匹配。

显然这俩都是排列,于是我们进行的是排列的匹配,这可以使用反序表进行。不难发现过程挺像 \(\rm KMP\),那么我们可以先处理出 \(p_i\) 的反序表,并且处理出 \(\rm KMP\) 使用的 \(nxt\) 数组,具体处理该数组,就是考虑在加入 \(p_i\) 时,在目前已经匹配到的长度中,比 \(p_i\) 大的数个数是否与已经匹配长度 \(len\)\(d_{len+1}\) 相同,这是可以使用 \(\rm BIT\) 维护,如果匹配失败,就暴力将 \([i-len, i-nxt[len]-1]\) 的数字撤回,使用均摊分析,该过程仍然是 \(\mathcal O(n)\) 的,由于使用 \(\rm BIT\),复杂度达到了 \(\mathcal O(n\log n)\).

匹配过程和处理 \(nxt\) 基本上是一样的,唯一需要注意的就是在达成一次匹配之后的撤回区间和 \(\rm KMP\) 失配时的撤回区间稍微有点不同,至于为什么可以自行思考一下。

叁、参考代码 ¶

# include <cstdio>
# include <algorithm>
# include <vector>
using namespace std;

# define NDEBUG
# include <cassert>

namespace Elaina {

# define rep(i, l, r) for(int i=(l), i##_end_=(r); i<=i##_end_; ++i)
# define drep(i, l, r) for(int i=(l), i##_end_=(r); i>=i##_end_; --i)
# define fi first
# define se second
# define mp(a, b) make_pair(a, b)
# define Endl putchar('\n')
# define mmset(a, b) memset(a, b, sizeof (a))
# define mmcpy(a, b) memcpy(a, b, sizeof (a))

typedef unsigned int uint;
typedef long long ll;
typedef unsigned long long ull;
typedef pair <int, int> pii;
typedef pair <ll, ll> pll;

template <class T> inline T fab(T x) { return x<0? -x: x; }
template <class T> inline void getmin(T& x, const T rhs) { x=min(x, rhs); }
template <class T> inline void getmax(T& x, const T rhs) { x=max(x, rhs); }
template <class T> inline T readin(T x) {
    x=0; int f=0; char c;
    while((c=getchar())<'0' || '9'<c) if(c=='-') f=1;
    for(x=(c^48); '0'<=(c=getchar()) && c<='9'; x=(x<<1)+(x<<3)+(c^48));
    return f? -x: x;
}

template <class T> inline void writc(T x, char s='\n') {
    static int fwri_sta[1005], fwri_ed=0;
    if(x<0) putchar('-'), x=-x;
    do fwri_sta[++fwri_ed]=x%10, x/=10; while(x);
    while(putchar(fwri_sta[fwri_ed--]^48), fwri_ed);
    putchar(s);
}

} using namespace Elaina;

const int maxn=1000000;

int a[maxn+5], b[maxn+5], n, m;
int p[maxn+5], t[maxn+5];

inline void input() {
    n=readin(1), m=readin(1);
    rep(i, 1, n) {
        a[i]=readin(1);
        p[a[i]]=i;
    }
    rep(i, 1, m) t[i]=b[i]=readin(1);
}

inline void getHash() {
    sort(t+1, t+m+1);
    rep(i, 1, m) b[i]=lower_bound(t+1, t+m+1, b[i])-t;
}

/** @warning the upper limit should be set to @p maxn */
namespace saya {

int c[maxn+5];

# define lowbit(i) ((i)&(-(i)))

inline void modify(int i, int v) {
    for(; i; i-=lowbit(i)) c[i]+=v;
}
inline int query(int i, int ret=0) {
    for(; i<=maxn; i+=lowbit(i)) ret+=c[i];
    return ret;
}
inline void clear() {
    rep(i, 1, maxn) c[i]=0;
}

} // using namespace saya;

int nxt[maxn+5], d[maxn+5];
inline void getNxt() {
    rep(i, 1, n) {
        d[i]=saya::query(p[i]);
        saya::modify(p[i], 1);
    }
    saya::clear(); nxt[1]=0, nxt[0]=-1;
    for(int i=2; i<=n; ++i) {
        int len=nxt[i-1];
        while(~len && d[len+1]!=saya::query(p[i])) {
            rep(j, i-len, i-nxt[len]-1) saya::modify(p[j], -1);
            len=nxt[len];
        }
        nxt[i]=len+1, saya::modify(p[i], 1);
    }
}

vector <int> ans;
inline void compare() {
    saya::clear();
    int len=0; // successfully matched length
    rep(i, 1, m) {
        while(~len && d[len+1]!=saya::query(b[i])) {
            rep(j, i-len, i-nxt[len]-1) saya::modify(b[j], -1);
            len=nxt[len];
        } assert(~len);
        ++len, saya::modify(b[i], 1);
        if(len==n) { // a successful match
            /** @warning pay attention to this special fallback interval */
            rep(j, i-len+1, i-nxt[len]) saya::modify(b[j], -1);
            len=nxt[len], ans.push_back(i-n+1);
        }
    }
}

inline void print() {
    writc(ans.size());
    for(auto x: ans) writc(x, ' ');
}

signed main() {
    // freopen("match.in", "r", stdin);
    // freopen("match.out", "w", stdout);
    input();
    getHash();
    getNxt();
    compare();
    print();
    return 0;
}

肆、关键の地方 ¶

进行排列的比较时,可以考察使用反序表,因为反序表唯一对应每一个排列,至于为什么我记得曾经自己给出过一种构造方法,通过这个构造方法确是可以说明一一映射的关系的。

另外,\(\rm KMP\) 也是可以魔改的,该题便是利用了 \(\rm KMP\) 判等条件的变化,另外对于 \(\rm KMP\) 复杂度分析稍微有点不熟,差点忘记摊还了......

posted @ 2021-08-31 21:34  Arextre  阅读(78)  评论(0编辑  收藏  举报