題目:bzoj4502.
題目大意:給定n
nn個單詞,求兩個在是這些單詞中任意乙個單詞字首的串拼起來能夠組合出的字串數量.
1 ≤n
≤104
1\leq n\leq 10^4
1≤n≤10
4,單詞長度≤
30\leq 30
≤30.
考慮最多有多少種可能,發現最多會有n
2n^2
n2種情況,但是這寫情況中會有重複.
考慮去掉重複,發現乙個若乙個字串s=s
1+s2
=s1′
+s2′
s=s_1+s_2=s'_1+s'_2
s=s1+
s2=
s1′
+s2′
且∣s1′
∣>∣s
1∣|s'_1|>|s_1|
∣s1′∣
>∣s
1∣時,那麼s
1s_1
s1必然是s1′
s'_1
s1′
的乙個字首,並且s2′
s'_2
s2′
必然是s
2s_2
s2的乙個字尾.
於是想到trie樹經常用於處理字首問題,而ac自動機的fail樹又可以用來處理字尾,我們考慮將這些串構成乙個ac自動機.
我們深入思考乙個串s會被重複構造的情況,如下圖:
我們發現,當乙個串s
ss被重複構造時,總會有乙個字首p
pp,使得s=p
+a+b
s=p+a+b
s=p+a+
b,我們發現只要讓串p+a
p+ap+
a和bb
b在trie中有,串p
pp和a+b
a+ba+
b在trie中也就會出現一次重複.
我們發現若要出現這種情況則必須滿足p+a
p+ap+
a能夠通過fail指標跳到a
aa,而且a+b
a+ba+
b也能通過fail指標跳到b
bb,會有這種重複的情況.
那麼考慮列舉串a+b
a+ba+
b,切掉它的最長字尾b
bb(使a
aa最短,避免重複計數),統計a
aa是多少個串的字尾即可得到一種方案重複了多少次.
接下來的問題就是如何實現了,列舉串我們可以dfs實現,切掉最長字尾就相當於切掉它的fail指標指向的串,剩餘部分可以通過記錄深度與dfs時每個深度代表的串來實現.統計乙個串是多少個串的字尾可以通過fail樹上的子樹大小siz
sizsi
z來實現.
時間複雜度o(∑
si)o(\sum s_i)
o(∑si
),若把每個字串的長度看成常數,則時間複雜度為o(n
)o(n)
o(n)
.**如下:
#include
using namespace std;
#define abigail inline void
typedef
long
long ll;
#define m(a) memset(a,0,sizeof(a))
const
int n=
1000000
,c=26
;struct trie
}tr[n+9]
;int cn;
void
build()
void
insert
(char
*c,int len)
}queue<
int>q;
int ord[n+9]
,co;
void
get_fail()
}for
(int i=co;i>=1;
--i)
tr[tr[ord[i]
].fail]
.siz+
=tr[ord[i]
].siz;
}int pos[n+9]
;ll ans;
void
dfs(
int x,
int dep)
int n;
char c[n+9]
;abigail into()
}abigail work()
abigail outo()
intmain()
BZOJ 3172 單詞 (AC自動機)
這道題是個裸的ac自動機,但是我還是調了很久qaq。首先如果我們直接用每個單詞來匹配的,時間不是很理想。這道題要用到ac自動機的衍生物 fail樹 我也是做這道題才知道有這個東西 fail樹有這麼乙個結論 乙個字串出現的次數等於以它為根節點的fail樹的子樹中所有節點的cnt的和。根據這個結論,我們...
bzoj3172 單詞 AC自動機
感覺以前寫過。bzoj上不去我也不知道 跑一遍ac自動機,每乙個節點儲存一下屬於多少字串,為它的權值。然後乙個節點表示的字串在整個字典中出現的次數相當於其在fail樹中的子樹的權值的和。ac自動機不要寫掛就好了。ac 如下 include include include define n 11000...
bzoj3172 單詞 AC自動機
有n個單詞組成了一篇文章,求每個單詞在這篇文章中出現了多少次。多個字串匹配的問題,建立ac自動機。如果某個單詞在i節點出現了,那麼在i節點fail指標所指節點也出現過。code include include using namespace std const int max n 1000005 s...