知識ポイント:
ACオートマトン、木、オフライン、フェンウィックの木、木の会長に失敗
質問の意味:
与えられた文字列の数、いくつかの質問(x、y)は、文字列をxは範囲(小文字)に加えて、Y-ストリームに何度も現れる1E5の範囲外です。
ソリューション:
与えられた「タイプライターは、読み取りモード」まず第一に、それによれば、我々は、トライにこのプロセスのシミュレーションをジャンプすることができます。出会いの小文字の文字は、その後、(新しい点を挿入することがない)、会ったジャンプ(\ B)\(各点の父親ポイントの数を維持するためにトライ必要に)彼の父に戻ってジャンプすることを、会った(P \ \)元のAビットが、元の文字列としてFAの現在の文字列を入れて、または番号を追加した場合)は、ノード(の終わりにマークします。これは、上記の方法によれば、の複雑さにバインドされていない場合、\(O(N ^ 2) \) 。
次に、どのように一致するように、単一の(x、y)を考えます。依頼する権利を有する注yの表示され、X、我々はいくつかのプロパティをクリアしなければならない回数です。
- ルートノードへの経路上のACAMノードは、経路上の各点は、現在の点の接頭辞です。これはときに、非常に明白であることを理解するためにトライして。
- ルートノードへのパスのツリーノードに失敗し、電流経路の各々上の点は、点サフィックスであり、サフィックスは単調に減少するシーケンスの長さでなければなりません。
ここでは、失敗の木は非常に美しい自然がいっぱいで、木の逆の形を接続するのに失敗するポインタで失敗しないものをツリーに追加します。
私たちは、だから、さらに2つの性質上を持って、我々はY、Xに表示されますが、サブツリーはYを含む(ツリーの対応するノードをyのノードの祖先失敗しているどのように多くのポイントツリーを失敗依頼することがあるxはその数を聞いて、見つけることができます自分自身)。
したがって、我々は、上記の情報を維持する方法を検討してください。
あなたがオンライン行う場合は、それぞれが失敗し、ツリーを横断、である(O(N ^ 2)\ \) 私たちはyはテキスト文字列として求められているので、あなたがソート処理yを選択することができるように、すべてのオフラインダウンを聞いているので、 。しかし、私は数字対応ACAMにハングアップ(FAは、そのレコードに同じ単語を挿入するときである)、xは、治療後ハング対応するY FAに直接ベクターを開くことを選択しました。
(ノードをルートとするサブツリーのようにDFSでの配列の連続ストレッチでなければなりません)DFSツリーの順序が取得に失敗考えてみましょう、次のように維持するために、データの問題フェンウィックツリー構造のシーケンスに変換することができます。毎回1 1に、(そうでなければ、彼らはありませんでしたいくつかのポイントに接続され、全ての息子を置くことを求めて前に再びバックアップに失敗することに注意)、DFSはトライオーバーが1である起こるノードの影響から回復しているのでXポイントは、ポイントは左のサブツリーを指すように右のサブツリーポイントを-xている(+1ポイントDFSツリーは、ツリーに対応する配列を失敗した後、現在のすべてのポイントに答えるように頼まポイントアレイ内の現在のポイントを置きます値)は、すべての真の息子を横断し、-1のままにします。
注意:
- 時間は失敗であるかを調べる、トライ何時です。ここでは、そのアナロジーSAMを考える個人的にサブストリング理解するために、サブストリングサフィックス=接尾辞接頭辞=接頭辞、それはトライ木をトラバースされていますが、DFSツリー内の順序の配列ツリーのようなメンテナンスが失敗。
- あなたが考えているなら、なぜ子供の場所の末尾に文字列のすべての番号を追加しますが、1ではありませんか?回数xの数は多くの時間が、それは一度だけカウントされていても、Yに表示されますので、それはある依頼する権利があります。
コード:
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<vector>
using namespace std;
const int maxn=100010;
int n,m,head[maxn],etot,tot=1,c[maxn],dfn[maxn],low[maxn],fa[maxn],cnt,ans[maxn],id[maxn];
struct node
{
int nxt,to;
}edge[maxn];
struct trie
{
int son[26],fail,tag,fa,ch[26];
}a[maxn];
struct pro
{
int x,id;
};
vector<pro>que[maxn];
queue<int>q;
char s[maxn];
int read()
{
int x=0;
char c=getchar();
while (c<48||c>57)
c=getchar();
while (c>=48&&c<=57)
x=(x<<1)+(x<<3)+(c^48),c=getchar();
return x;
}
void insert(char *s)
{
int i,u=1,len=strlen(s),k;
a[1].fa=1;
for (i=0;i<len;i++)
{
if (s[i]>='a'&&s[i]<='z')
{
k=s[i]-'a';
if (!a[u].son[k])
{
a[u].son[k]=(++tot);
a[tot].fa=u;
}
u=a[u].son[k];
}
if (s[i]=='B')
u=a[u].fa;
if (s[i]=='P')
{
id[++n]=u;
fa[n]=n;
if (a[u].tag)
fa[n]=a[u].tag;
else
a[u].tag=n;
}
}
}
void getfail()
{
int i,u,v,fafail;
for (i=0;i<=25;i++)
a[0].son[i]=1;
a[1].fail=0;
q.push(1);
while (!q.empty())
{
u=q.front();
q.pop();
fafail=a[u].fail;
for (i=0;i<=25;i++)
{
v=a[u].son[i];
if (!v)
a[u].son[i]=a[fafail].son[i];
else
{
a[v].fail=a[fafail].son[i];
q.push(v);
}
}
}
}
void add(int u,int v)
{
edge[++etot]=(node){head[u],v};
head[u]=etot;
}
int lowbit(int x)
{
return x&-x;
}
void update(int x,int val)
{
for (;x<=cnt;x+=lowbit(x))
c[x]+=val;
}
int query(int x)
{
int res=0;
for (;x;x-=lowbit(x))
res+=c[x];
return res;
}
void DFS(int u)
{
int i;
dfn[u]=(++cnt);
for (i=head[u];i;i=edge[i].nxt)
DFS(edge[i].to);
low[u]=cnt;
}
void dfs(int u)
{
update(dfn[u],1);
int i,siz=que[u].size();
for (i=0;i<siz;i++)
ans[que[u][i].id]=query(low[que[u][i].x])-query(dfn[que[u][i].x]-1);
for (i=0;i<=25;i++)
if (a[u].ch[i])
dfs(a[u].ch[i]);
update(dfn[u],-1);
}
int main()
{
int i,j,u,v;
scanf("%s",s);
insert(s);
for (i=1;i<=tot;i++)
for (j=0;j<=25;j++)
a[i].ch[j]=a[i].son[j];
getfail();
for (i=1;i<=tot;i++)
if (a[i].fail!=i&&a[i].fail>=1)
add(a[i].fail,i);
DFS(1);
m=read();
for (i=1;i<=m;i++)
{
u=id[fa[read()]],v=id[fa[read()]];
que[v].push_back((pro){u,i});
}
dfs(1);
for (i=1;i<=m;i++)
printf("%d\n",ans[i]);
return 0;
}