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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

【bzoj2434】[Noi2011]阿狸的打字机 AC自动机+Dfs序+树状数组

發布時間:2025/7/14 编程问答 23 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【bzoj2434】[Noi2011]阿狸的打字机 AC自动机+Dfs序+树状数组 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

題目描述

阿貍喜歡收藏各種稀奇古怪的東西,最近他淘到一臺老式的打字機。打字機上只有28個按鍵,分別印有26個小寫英文字母和'B'、'P'兩個字母。
經阿貍研究發現,這個打字機是這樣工作的:
l 輸入小寫字母,打字機的一個凹槽中會加入這個字母(這個字母加在凹槽的最后)。
l 按一下印有'B'的按鍵,打字機凹槽中最后一個字母會消失。
l 按一下印有'P'的按鍵,打字機會在紙上打印出凹槽中現有的所有字母并換行,但凹槽中的字母不會消失。
例如,阿貍輸入aPaPBbP,紙上被打印的字符如下:
a
aa
ab
我們把紙上打印出來的字符串從1開始順序編號,一直到n。打字機有一個非常有趣的功能,在打字機中暗藏一個帶數字的小鍵盤,在小鍵盤上輸入兩個數(x,y)(其中1≤x,y≤n),打字機會顯示第x個打印的字符串在第y個打印的字符串中出現了多少次。
阿貍發現了這個功能以后很興奮,他想寫個程序完成同樣的功能,你能幫助他么?

輸入

輸入的第一行包含一個字符串,按阿貍的輸入順序給出所有阿貍輸入的字符。

第二行包含一個整數m,表示詢問個數。

接下來m行描述所有由小鍵盤輸入的詢問。其中第i行包含兩個整數x, y,表示第i個詢問為(x, y)。

輸出

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

樣例輸入

aPaPBbP
3
1 2
1 3
2 3

樣例輸出

2
1
0


題解

AC自動機

題目中打字的方法其實就是在建立Trie樹,根據這個可以建出一棵Trie樹。

首先必須要知道,x串在y串中出現的次數,就是y串中可以通過fail鏈到達x串的節點個數,

也就是fail樹中,x串的子樹中y串中節點的個數。

接下來的問題比較復雜。

怎樣求fail樹中x串的子樹中y串中節點的個數?也即y串中節點有幾個在x串中。

這需要dfs序,l與r之間是對應子樹,可以用樹狀數組進行單點修改,區間查詢。

在AC自動機中遍歷一遍,遍歷到y時,把l[y]加1,退出時把r[y]都減1。

那么可以發現:這就是給定的打字方式!

'a'~'z':添加字符 ? ? ? ? ? ? ?'a'~'z':數組加1

'B':刪除字符 ? ? ? ?=> ? ? ?'B':數組減1

'P':打印字符 ? ? ? ? ? ? ? ? ? 'P':處理詢問

于是再次對原字符串進行處理,即可得解。

#include <cstdio> #include <cstring> #include <queue> #include <vector> #define N 1000001 using namespace std; queue<int> q; int nt[N][26] , fa[N] , fail[N] , cnt[N] , tot = 1 , pos[N] , p , c , head[N] , to[N] , nextn[N] , l[N] , r[N] , f[N << 1] , k , ans[N]; char str[N]; vector<int> son[N]; void add(int x , int y) {to[++c] = y;nextn[c] = head[x];head[x] = c; } void build() {int u , t , i;q.push(1);while(!q.empty()){u = q.front();q.pop();for(i = 0 ; i < 26 ; i ++ ){if(nt[u][i]){q.push(nt[u][i]);t = fail[u];while(t && !nt[t][i])t = fail[t];if(t) fail[nt[u][i]] = nt[t][i];else fail[nt[u][i]] = 1;}}} } void dfs(int x) {l[x] = ++k;int i;for(i = 0 ; i < (int)son[x].size() ; i ++ )dfs(son[x][i]);r[x] = ++k; } void update(int x , int a) {int i;for(i = x ; i <= k ; i += i & -i)f[i] += a; } int query(int x) {int i , ans = 0;for(i = x ; i ; i -= i & -i)ans += f[i];return ans; } void solve() {int now = 1 , id = 0 , i , j , len = strlen(str);update(l[1] , 1);for(i = 0 ; i < len ; i ++ ){if(str[i] == 'P')for(j = head[++id] ; j ; j = nextn[j])ans[j] += query(r[pos[to[j]]]) - query(l[pos[to[j]]] - 1);else if(str[i] == 'B'){update(r[now] , -1);now = fa[now];}else{now = nt[now][str[i] - 'a'];update(l[now] , 1);}} } int main() {int i , t = 1 , len , x , y , m;scanf("%s" , str);len = strlen(str);for(i = 0 ; i < len ; i ++ ){if(str[i] == 'P'){cnt[t] ++ ;pos[++p] = t;}else if(str[i] == 'B')t = fa[t];else{if(!nt[t][str[i] - 'a'])nt[t][str[i] - 'a'] = ++tot;fa[nt[t][str[i] - 'a']] = t;t = nt[t][str[i] - 'a'];}}build();for(i = 1 ; i <= tot ; i ++ )son[fail[i]].push_back(i);dfs(1);scanf("%d" , &m);for(i = 1 ; i <= m ; i ++ ){scanf("%d%d" , &x , &y);add(y , x);}solve();for(i = 1 ; i <= m ; i ++ )printf("%d\n" , ans[i]);return 0; }

轉載于:https://www.cnblogs.com/GXZlegend/p/6265087.html

總結

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

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