NOI2011阿狸的打字機

2021-08-18 02:25:36 字數 2376 閱讀 4007

剛學完字串演算法做一做題,這道題的質量的確很高,做完以後感覺對ac自動機有長進

一下的神仙思路來自yyb dalao%%%,蒟蒻開始只想到了40分暴力,全程靠題解

step1

首先直接處理出所有的串再裸kmp好寫,但是覺得得分應該不高,也沒有人說能拿多少分

這個題正解的第一步是要想到ac自動機,準確地說和fail指標關係密切

首先明確乙個定理,如果當前沿著目標串a在trie樹下向下走,走到了乙個節點,發現這個節點指向b串的末尾,說明a串一定包含b串。

所以乙個樸素的想法已經出來了,a串在trie樹上的每個節點都沿失配指標一直跳,如果能跳到b串的末尾,這個節點就是合格的。所有合格節點的數目就是b串在a串上的出現次數(所有沿著失配指標跳到的節點都是b串末尾)。trie樹中的每個節點可以記錄在trie樹中的fa,在記錄一下每個串末尾節點編號。所以查詢每個y串的時候,沿著它的末尾節點一路跳到根就可以了。

step2

可以發現每次跳fail指標只為找一種x串末尾實在太浪費了,所以可以把所有詢問按y排序,所有y相同的詢問一併處理,y串tire樹中的每乙個節點沿著失配指標跳到的所有節點,如果作為某串結尾,就用桶統計一下這個串被訪問了多少次。這樣會提高一部分效率,然而良心出題人也把這個聰明的操作提高了三十分,現在可以拿到70分

step3

可以發現每乙個節點只有乙個fail指標,所以這就滿足樹的條件,可以把fail指標取反成邊,構造一顆fail樹。所以問題由y上的每個節點能有幾個沿fail指標跳到x串末尾,變為了x串末尾能跳到幾個y串節點。可以發現,x串末尾節點能跳到的所有的y串上的節點,在fail樹中都是它的子節點,所以問題就相當於x串末尾節點的fail樹子樹中有幾個是y串上的點。可以發現乙個節點,它的子樹dfs序是連續的,所以就可以用資料結構維護一下(我用的常數較小的樹狀陣列),y串遍歷到的每乙個節點插入到資料結構中+1,查詢時統計一下區間和。這個做法還是70分

step4

這就是最神仙操作的正解了。我們發現每次把y串挨個節點插入到資料結構中實在是有點浪費,所以就會想到有沒有高效一些的操作。這裡給出我在luogu上看到的兩種做法

1首先是yyb dalao的,yyb大佬是把trie樹上每個節點多打了乙個標記,記錄這個節點是哪個串的末尾。然後把整個trie樹遍歷一遍,因為tire樹滿足乙個性質,就是如果能走到乙個串的末尾節點,那麼當前從根走過來的路徑就是這個串,不多字元不少字元。

所以就把這個trie樹的每個節點都遍歷一遍,遍歷到它時把這個節點的dfs序在資料結構中對應的位置+1,退出前再-1

中間判斷一下這個節點是不是乙個y串的末尾,如果是,就在此時求對應x串結尾的子樹和

這個方法對trie樹掌握的已經相當神了,我第一遍看完以後簡直跪了,然而我發現

這樣的話會被重複串hack掉,

比如第二個串和第四個串一樣,這樣第二個串末尾節點標記就會被第四個節點沖掉,這樣就會認為它只是第四個串的末尾,第二個串是不存在的,所以如果有和第二個串有關的查詢,答案就會變成0

2這個做法是守望dalao的**。為了避免剛才那種情況,我們就模擬一下這個打字機,統計當前串的序號。每當遇到b退格時,上乙個字元對應節點dfs序在資料結構中-1。每當遇到新的小寫字元,資料結構中該點dfs序對應位置+1。

當遇到p時,說明到了乙個串的末尾。那麼當前生成過幾個串的數目+1,就是這個串的編號。所幸我們把詢問按y排序,所以序號靠前的串早處理。具體操作就是碰到p時,如果當前生成的序號編號為num,那麼對於每個num==y的詢問,都處理出x的子樹和

#include#include#include#include#include#define maxn (200010)

using namespace std;

int l,q_num,size,fa[maxn],s[maxn][30],f[maxn],end[maxn],n;

int dfn[maxn],low[maxn],h[maxn],m1,dfs_clock,ans[maxn];

struct edge

}q[maxn*4];

void addedge(int x,int y)

struct treearray

void change(int pos,int x)

} int sum(int pos)

return res;

} int query(int l,int r)

}ta;

struct qu

} }}void build() }}

void dfs(int x)

low[x]=dfs_clock;

}void work()

if (st[i]=='p')

continue;

} d=st[i]-'a';

rt=s[rt][d]; ta.change(dfn[rt],1); }}

int main()

NOI2011阿狸的打字機

阿狸喜歡收藏各種稀奇古怪的東西,最近他淘到一台老式的打字機。打字機上只有28個按鍵,分別印有26個小寫英文本母和 b p 兩個字母。經阿狸研究發現,這個打字機是這樣工作的 l 輸入小寫字母,打字機的乙個凹槽中會加入這個字母 這個字母加在凹槽的最後 l 按一下印有 b 的按鍵,打字機凹槽中最後乙個字母...

NOI2011 阿狸的打字機

阿狸喜歡收藏各種稀奇古怪的東西,最近他淘到一台老式的打字機。打字機上只有28個按鍵,分別印有 26個小寫英文本母和 b p 兩個字母。經阿狸研究發現,這個打字機是這樣工作的 輸入小寫字母,打字機的乙個凹槽中會加入這個字母 按 p 前凹槽中至少有乙個字母 按一下印有 b 的按鍵,打字機凹槽中最後乙個字...

NOI2011 阿狸的打字機

調了下午到晚上 從0 40 70 100 心力交瘁 40分做法 直接在fail樹上暴力跳,修改節點值,樹狀陣列查詢時間複雜度n 2log 70分做法 接近正解,離線詢問,在對應的位置打標記,dfs的時候可以遍歷到這個點的時候,對這個點所有的詢問都可以同時處理出來 100分做法 讀入的時侯可以優化一下...