洛谷 5826 模板 子串行自動機 題解

2021-10-01 15:27:18 字數 3411 閱讀 6010

部落格觀賞效果更佳

你以為我只是單純的子串行自動機嗎?其實我是是子串行自動機+可持久化陣列噠!

但是我看見乙個大神給出了乙個特別神仙又巧妙的思路!我不禁要寫一篇題解記錄下這神奇的思路!

而且**賊短哦~比可持久化陣列好寫到不知道多少倍呢www

給定乙個序列a

aa長度<=1e5,還要一些要詢問的字串b

bb,長度和<=1

e6

<=1e6

<=1

e6。對於每個b

bb,詢問其是否是a

aa的子串行。序列中的每個值都在1e5

1e51e

5以內。

在暴力匹配子串行的時候,用鍊錶(或者vector)記錄哪些詢問的答案會被更新即珂。複雜度o(1

e6+1

e5

)o(1e6+1e5)

o(1e6+

1e5)

。正片開始。我們知道,樸素的子串行的查詢是o(∣

a∣×∣

b∣

)o(|a|\times |b|)

o(∣a∣×

∣b∣)

的。我們大約是這樣寫的:

int p=1;

f(i,1,

|a|)

if(a[i]

==b[p]

)++p;

然後判斷是否p==|b|。其中p

pp表示我們當前匹配到了哪乙個位置。

子串行自動機:設nxt

[i][

j]

nxt[i][j]

nxt[i]

[j]表示i

ii往後第乙個值為j

jj的出現在哪乙個位置。這樣的確方便查詢,但是這裡值域1e5

1e51e

5,這個做法顯然沒救了。

考慮優化第乙個方法:把所有的詢問放到一塊來處理。那麼,當我們找到乙個a[i]的時候,所有滿足b[p]==a[i]的p都會執行p++操作。

那麼我們只要記錄所有b[p]==a[i]的b[p]都在哪些位置就好了。所以,首先要用乙個鍊錶(鏈式前向星),把b

bb的值按輸入順序串起來。然後要用乙個鍊錶把b

bb按值域分類。具體的,記hea

d[i]

head[i]

head[i

]表示b

bb中值為i

ii的數最後乙個出現的位置,nxt

[i

]nxt[i]

nxt[i]

表示b

bb中前乙個和b[i

]b[i]

b[i]

相同的。然後我們令cur

=hea

d[xx

x]

cur=head[***]

cur=he

ad[x

xx],不斷的令cur

=nxt

[cur

]cur=nxt[cur]

cur=nx

t[cu

r],就珂以找到b

bb中所有值為xxx

***xx

x的位置了。

然後,我們在把b

bb按輸入順序穿起來的時候,對於每個b

bb的最後乙個字元,我們令它的下乙個值為−i-i

−i,其中i

ii為這個b

bb在輸入中的編號。這樣,

「既判定了匹配到了末尾,又判定了當前所在的字串(的編號,博主注),可謂一舉兩得~」(作者原話)

關於如何處理詢問:

我們以a

aa為基準,不斷在b

bb中找到匹配。對於每個a[i

]a[i]

a[i]

,我們通過遍歷前向星找到b

bb中值為a[i

]a[i]

a[i]

的位置。如果這個位置是最後乙個位置,那麼這個b

bb就匹配成功,我們標記它是a

aa的子串行。否則,我們把原來的指標指向b[i

]b[i]

b[i]

的刪掉,連線上b[i

+1

]b[i+1]

b[i+1]

,就是實現上面的p++

p++p+

+操作。

然後我們發現,如果匹配成功了,也要刪掉指標。那就不如在遍歷到a[i

]a[i]

a[i]

的時候,直接全部清空好了。(鏈式前向星清空很簡單,只要令hea

d[a[

i]]=

0head[a[i]]=0

head[a

[i]]

=0即珂。)

#include

using

namespace std;

namespace flandre_scarlet

int type,n,q,m;

//type:沒什麼用,給你騙分用的。滿分的**是不需要騙分的。

int a[n]

;void

input()

int len[n]

,nxtb[n]

,valb[n]

,cntb;

//按輸入順序記錄b的鍊錶們www

//len[i]: 第i個詢問中b的長度

//nxtb[i]: 用來把b串起來。記錄下標。一般nxt[i]=i+1。如果i到末尾了,那麼nxtb[i]=-id。

//id為這個b的編號。這一步的妙處上面說了

//valb[i]: 和nxtb區別,這個是記錄b的值。

//cntb: 總共有多少個點。到最後,它的值就等於len[i]的和。

int node[n]

,head[n]

,nxt[n]

,cntval;

//按b的值域分類記錄的鍊錶們www

//node[i]: 記錄點在b中的編號,方便求出nxtb

//head[i]: 記錄b中最後乙個值為i的位置(這邊的位置是在鍊錶中的位置,而不是b中的位置)。

//nxt[i]: 記錄b中下乙個和i的值相同的位置。

bool cxk[n]

;//cxk[i]: 第i個詢問是否是子串行

void

add(

int u,

int v)

//b中值為u的位置新增上乙個v

void

soviet()

nxtb[cntb]

=-i;

//好東西,上面講了}f

(i,1

,n)}

f(i,

1,q)

}#define flan void

flan ismywife()

}int

main()

Subsequence(序列自動機模板題)

題目大意 給你乙個字串,然後再給你m個字串,然後問你在第乙個字串中不連續的子串能不能構成輸入的子串。具體思路 構建乙個序列自動機就可以了。剛接觸,記錄下 ac 1 include2 using namespace std 3 define ll long long 4 define inf 0x3f...

序列自動機 模板

南昌邀請賽網路賽m題 subsequence 題意 給你乙個字串s,長度小於1e5,然後n次詢問,n小於1e5,每次輸入乙個字串t,問t是不是s的子串行。t長度小於1000 input abcdefg 3abc adgcba output yes yesno 思路 序列自動機其實就是先預處理出來乙個...

序列自動機模板

題意 有乙個字串s1,現在給你n個字串s2,每次你需要回答s2是否是s1的子串行。解題心得 序列自動機其實就是先預處理出來乙個陣列,next i j 表示在位置 i 的後面第乙個字元 j 所在的位置,預處理出next陣列的複雜度就是log n 26 每次詢問就是log m 的複雜度 m是每次詢問字串...