日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 人文社科 > 生活经验 >内容正文

生活经验

【BZOJ2434】[NOI2011]阿狸的打字机 AC自动机+DFS序+树状数组

發布時間:2023/11/27 生活经验 34 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【BZOJ2434】[NOI2011]阿狸的打字机 AC自动机+DFS序+树状数组 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

【BZOJ2434】[NOI2011]阿貍的打字機

Description

?阿貍喜歡收藏各種稀奇古怪的東西,最近他淘到一臺老式的打字機。打字機上只有28個按鍵,分別印有26個小寫英文字母和'B'、'P'兩個字母。
經阿貍研究發現,這個打字機是這樣工作的:

l 輸入小寫字母,打字機的一個凹槽中會加入這個字母(這個字母加在凹槽的最后)。
l 按一下印有'B'的按鍵,打字機凹槽中最后一個字母會消失。
l 按一下印有'P'的按鍵,打字機會在紙上打印出凹槽中現有的所有字母并換行,但凹槽中的字母不會消失。
例如,阿貍輸入aPaPBbP,紙上被打印的字符如下:

a

aa

ab

我們把紙上打印出來的字符串從1開始順序編號,一直到n。打字機有一個非常有趣的功能,在打字機中暗藏一個帶數字的小鍵盤,在小鍵盤上輸入兩個數(x,y)(其中1≤x,y≤n),打字機會顯示第x個打印的字符串在第y個打印的字符串中出現了多少次。
阿貍發現了這個功能以后很興奮,他想寫個程序完成同樣的功能,你能幫助他么?

Input

輸入的第一行包含一個字符串,按阿貍的輸入順序給出所有阿貍輸入的字符。 第二行包含一個整數m,表示詢問個數。 接下來m行描述所有由小鍵盤輸入的詢問。其中第i行包含兩個整數x, y,表示第i個詢問為(x, y)。

Output

輸出m行,其中第i行包含一個整數,表示第i個詢問的答案。

Sample Input

aPaPBbP 3 1 2 1 3 2 3

Sample Output

2 1 0

HINT

1<=N<=10^5 1<=M<=10^5 輸入總長<=10^5

題解:先構建fail樹,然后對于一個詢問a b,我們就可以將其轉化成 在以a為根的子樹(這里的子樹指fail樹)中,有多少個節點在單詞b中出現過,于是我們自然想到要用離線處理來搞。就是在DFS遍歷fail樹的時候,分別記錄 從上面查詢到該點 和 從下面回溯到該點 時,有多少個節點在b中出現過,兩者的差就是我們要的答案,我們只需要把 詢問 以 鏈表 的形式存儲到a上就可以了。

但我們發現一個問題,對于某一個節點,它可能存在于很多個單詞之中,那我們搜到這個點時就要把這些單詞出現的次數全部+1,怎么辦?

我們不妨分析一下AC自動機的性質,對于所有包含節點x的單詞,他們的結束點一定在以x為根的子樹中(這里的子樹指AC自動機)。仔細觀察發現,這些結束點在AC自動機的DFS序中一定是一段連續的區間(中間沒有其它結束點)。利用這個性質,我們就可以對于每一個節點x,求出它對應的區間,然后將整個區間+1,這個可以用樹狀數組實現。

(以上都是個人想法,也許麻煩了,但事實證明可以AC)

這么做的惡心之處就在于:1.對于一個結束點,既要知道它在AC自動機中的的編號,又要知道它在詢問中的編號,還要知道它在DFS序中的編號,特別容易搞混。2.DFS要進行兩遍,一遍在AC自動機上搞,一遍在fail樹上搞。3.我們一開始求出來的fail樹是從兒子指向父親的,我們還要正向再建一遍樹。4.一個結束點可能對應多個單詞。

#include <cstdio>
#include <cstring>
#include <iostream>
#include <queue>
using namespace std;
const int maxn=100010;
struct node
{int ch[26],fail,l,r,cnt;
}p[maxn];
char str[maxn];
int sta[maxn],tp,s[maxn],pos[maxn];
int n,tot,len,sum,now;
int t1[maxn],n1[maxn],h1[maxn],ans[maxn],n2[maxn],h2[maxn];
int tree[maxn];
queue<int> q;
void updata(int x,int v)
{for(int i=x;i<=sum;i+=i&-i)    tree[i]+=v;
}
int query(int x)
{int i,ret=0;for(i=x;i;i-=i&-i)    ret+=tree[i];return ret;
}
void dfs1(int x)    //遍歷AC自動機 
{p[x].l=p[x].r=now;    //如果這個點是結束點,那么now就是該點在DFS序中的順序now+=p[x].cnt;    //[l,r]就是這個點所影響的單詞的區間for(int i=0;i<26;i++){if(p[x].ch[i]){dfs1(p[x].ch[i]);p[x].r=max(p[x].r,p[p[x].ch[i]].r);}}
}
void build()
{int i,j,u,t;q.push(1);while(!q.empty()){u=q.front(),q.pop();for(i=0;i<26;i++)    //這里不要用fail修改兒子了,因為還要DFS {if(!p[u].ch[i])    continue;q.push(p[u].ch[i]);t=p[u].fail;while(!p[t].ch[i]&&t)    t=p[t].fail;if(t)    p[p[u].ch[i]].fail=p[t].ch[i];else    p[p[u].ch[i]].fail=1;}}
}
void dfs(int x)    //遍歷fail樹 
{int i;for(i=h1[x];i;i=n1[i]){ans[i]-=query(p[t1[i]].l);}updata(p[x].l,1);updata(p[x].r+1,-1);for(i=h2[x];i;i=n2[i]){dfs(i);}for(i=h1[x];i;i=n1[i])    //兩次差值就是答案,意會一下 {ans[i]+=query(p[t1[i]].l);}
}
int main()
{scanf("%s%d",str,&n);len=strlen(str);int i,j,k,u,t,a,b;u=sta[++tp]=1;tot=1;for(i=0;i<len;i++)    //用棧搞一搞,輸入其實很簡單 {if(str[i]=='B')    u=sta[--tp];else if(str[i]=='P')    p[u].cnt++,pos[++sum]=u;else{if(!p[u].ch[str[i]-'a'])    p[u].ch[str[i]-'a']=++tot;u=p[u].ch[str[i]-'a'];sta[++tp]=u;}}for(i=1;i<=n;i++){scanf("%d%d",&a,&b);    //將詢問存儲到a節點上 t1[i]=pos[b];n1[i]=h1[pos[a]];h1[pos[a]]=i;}build();now=1;dfs1(1);for(i=2;i<=tot;i++)    //重建fail樹 {n2[i]=h2[p[i].fail];h2[p[i].fail]=i;}dfs(1);for(i=1;i<=n;i++)    printf("%d\n",ans[i]);return 0;
}

?

轉載于:https://www.cnblogs.com/CQzhangyu/p/6262165.html

總結

以上是生活随笔為你收集整理的【BZOJ2434】[NOI2011]阿狸的打字机 AC自动机+DFS序+树状数组的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。