@loj - 2507 @「CEOI2011」マッチング


@説明@

整数のシーケンスのために\((A_1、A_2、... 、A_N)\) と1からnに配置されている\((P_1、P_2、...、P_N)\)と呼ばれる、\((A_1、A_2、... 。、A_N)\)に沿った\((P_1、P_2、...、P_N)\) 場合に限り:

(1)任意の2つの数の{A}は互いに異なります。
(2)\((A_1、A_2、... 、A_N)\) 昇順になります後(\(A_ {P_1}、 A_ {P_2}、...、A_ {P_N}) \) 。

1〜nは今シーケンス{P}の配置を説明する\(H_1、H_2、...、h_m \)は、サブストリングhは{P}に沿って配置されているかを決定します。

入力フォーマット
は正の整数で分離された2つの空間の最初の行のN、M。
正の整数n個の第2の列は、スペースで区切られた、配列pを示しています。
3番目の列領域は正の整数mを、配列hを分離しました。

出力形式
整数kの最初の行のは、コンプライアンス{P}のサブストリングの数を示します。
正の整数k離れた空間の第2行は、サブストリングの開始位置(1から始まる番号)を表します。昇順で出力セットの位置。具体的には、K = 0は、その後、あなたはまた、空白行を出力する必要があります。

サンプル入力
。5 10
2. 4. 3. 1. 5
。5. 6. 8 3. 1 7 10 11 9 12は、
サンプル出力
2
2 6

そしてプロンプトデータの範囲は
2 <= N <= M < = 1000000; 1 <= HI <= 10 ^ 9; 1 <= PI <= nです。
そして{H}互いに異なる元素、{P}は順列であることを保証します。

@解決@

最初のステップの変換の問題:検索{Q}よう\(Q_ P_I} = {I \) 即ち、{P}は逆の順列です。
次に、サブ大会{P}は離散1〜後ストリングに相当するN Qに等しいことができます。

あなたが離散考慮しない場合は、直接KMPに、古典的な部分文字列のマッチング問題です。
それKMP拡張することができるかどうか、離散均質後、ある同形としてサブストリング決意法、もし?

トンの均質で知られている文字列と文字列、Aの末尾に追加S、T、S + Aが決定され、T + B同型ならばされるの末尾にBを追加:必要KMP同型の文を考えるとき。
sおよびtは、AおよびBは、構造に追加することができた後にのみ、同一の構成を有するので、同じままです。
T中のSで決定されたランクに相当する(Sは数よりも小さい)= Bランク(T bの数よりも小さい)であってもよいです。

検索ランキングは、離散+ツリーの配列に直接バランスの取れた木が、この質問することができます。
フェンウィックツリーを維持するため注意が必要で要素を移動するときに失敗ジャンプKMP。
しかし、証拠の複雑さは変更するつもりはありません。KMPまたはO(N)、ツリーアレイのセットは、O(nlogn)です。

@acceptedコード@

#include <cstdio>
#include <vector>
#include <algorithm>
using namespace std;
const int MAXN = 1000000;
int n, m;
int t[2][MAXN + 5];
int lowbit(int x) {return x & -x;}
void update(int x, int k, int type) {
    for(int i=x;i<=m;i+=lowbit(i))
        t[type][i] += k;
}
int sum(int x, int type) {
    int ret = 0;
    for(int i=x;i;i-=lowbit(i))
        ret += t[type][i];
    return ret;
}
int d[MAXN + 5], p[MAXN + 5], h[MAXN + 5];
void discrete() {
    for(int i=1;i<=m;i++) d[i] = h[i];
    sort(d + 1, d + m + 1);
    for(int i=1;i<=m;i++)
        h[i] = lower_bound(d + 1, d + m + 1, h[i]) - d;
}
int f[MAXN + 5];
void get_f() {
    f[1] = 0;
    int ri = 0, le = 2;
    for(int i=2;i<=n;i++) {
        int j = f[i-1];
        while( sum(p[j+1], 0) != sum(p[i], 1) ) {
            while( ri != f[j] )
                update(p[ri--], -1, 0), update(p[le++], -1, 1);
            j = f[j];
        }
        f[i] = j + 1;
        update(p[++ri], 1, 0), update(p[i], 1, 1);
    }
}
vector<int>ans;
void get_ans() {
    for(int i=1;i<=m;i++)
        t[0][i] = t[1][i] = 0;
    int le = 1, ri = 0, j = 0;
    for(int i=1;i<=m;i++) {
        while( sum(p[j+1], 0) != sum(h[i], 1) ) {
            while( ri != f[j] )
                update(p[ri--], -1, 0), update(h[le++], -1, 1);
            j = f[j];
        }
        j++;
        update(p[++ri], 1, 0), update(h[i], 1, 1);
        if( j == n ) {
            ans.push_back(i-n+1);
            while( ri != f[j] )
                update(p[ri--], -1, 0), update(h[le++], -1, 1);
            j = f[j];
        }
    }
}
int main() {
    scanf("%d%d", &n, &m);
    for(int i=1;i<=n;i++) {
        int x; scanf("%d", &x);
        p[x] = i;
    }
    for(int i=1;i<=m;i++) scanf("%d", &h[i]);
    discrete(), get_f(), get_ans();
    printf("%d\n", (int)ans.size());
    for(int i=0;i<(int)ans.size();i++)
        printf("%d%c", ans[i], (i + 1 == ans.size() ? '\n' : ' '));
    if( ans.empty() ) puts("");
}

詳細@

これは、直接であるカントールは、カウントを開始しましたので、私は、文字列のハッシュ、ハッシュについて考えています。
この問題もバランスの取れたツリーを作成し、ハッシュプレフィックスと減算、ダイナミックなメンテナンス感触を見つけることができないし、あきらめました。
結局のところ、バランスに関して我々はまだコードツリーの簡単フェンウィックツリーバーを好みます。

おすすめ

転載: www.cnblogs.com/Tiw-Air-OAO/p/11845570.html