ディレクトリ
@説明@
整数のシーケンスのために\((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("");
}
詳細@
これは、直接であるカントールは、カウントを開始しましたので、私は、文字列のハッシュ、ハッシュについて考えています。
この問題もバランスの取れたツリーを作成し、ハッシュプレフィックスと減算、ダイナミックなメンテナンス感触を見つけることができないし、あきらめました。
結局のところ、バランスに関して我々はまだコードツリーの簡単フェンウィックツリーバーを好みます。