文章目錄
1. 題目描述
1.1. Limit
Time Limit: 2000/1000 MS (Java/Others)
Memory Limit: 131072/131072 K (Java/Others)
1.2. Problem Description
In the modern time, Search engine came into the life of everybody like Google, Baidu, etc.
Wiskey also wants to bring this feature to his image retrieval system.
Every image have a long description, when users type some keywords to find the image, the system will match the keywords with description of image and show the image which the most keywords be matched.
To simplify the problem, giving you a description of image, and some keywords, you should tell me how many keywords will be match.
1.3. Input
First line will contain one integer means how many cases will follow by.
Each case will contain two integers means the number of keywords and keywords follow.
Each keyword will only contains characters ‘a’-‘z’, and the length will be not longer than .
The last line is the description, and the length will be not longer than .
1.4. Output
Print how many keywords are contained in the description.
1.5. Sample Input
1
5
she
he
say
shr
her
yasherhs
1.6. Sample Output
3
1.7. Source
2. 解讀
AC自動機 (Aho-Corasick automaton)
的模板題。
AC自動機
使用了 Trie樹
的結構和 KMP算法
的思想,將所有模式串 用Trie樹
進行存儲,並且對每個節點,構造 Fail數組
,這個 Fail數組
和 KMP算法
中的 Next數組
有些類似,但不完全一樣。
其中 表示模式串 在 Trie樹
中的開始位置, 表示我們判斷的是當前模式串分支 的第 個節點, 表示 中以 爲終點的最長後綴與 Trie樹
中的第 個位置對應的模式串 的前綴相匹配。
爲左閉右開區間, 爲後綴的長度, 表示我們要找的是最長匹配後綴。
使用以下輸入爲例
1
5
she
he
say
shr
her
yasherhs
首先構建 Trie樹
,如圖1
。每個節點左邊的數字爲節點序號,右邊爲節點存儲的字符。
然後通過 DFS
獲取 數組,標號爲 1-2
的子串 sh
,其後綴 h
與標號爲 4
的前綴相匹配;標號爲 1-3
的子串 she
,其後綴 he
與標號爲 4-5
的前綴相匹配。
數組中的其他成員都指向根結點,即 。
在代碼中加入輸出語句,可以看到 DFS
構建 數組的具體過程。
front: 4
char: e fail: 0
front: 1
char: a fail: 0
char: h fail: 4
front: 5
char: r fail: 0
front: 6
char: y fail: 0
front: 2
char: e fail: 5
char: r fail: 0
front: 9
front: 7
front: 3
front: 8
3. 代碼
#include <iostream>
#include <queue>
#include <string.h>
using namespace std;
const int maxN = 5e5 + 1; // 模式串長度 * 模式串數量
const int maxM = 1e6 + 1; // 目標串長度
const int chSize = 26; // 字符集大小
class AcAutomaton {
public:
int trie[maxN][chSize];
int vis[maxN], fail[maxN];
int tot;
// 初始化
void init()
{
memset(vis, 0, sizeof vis);
memset(trie, 0, sizeof trie);
tot = 0;
}
// 插入
void insert(char* str)
{
int len = strlen(str);
int pos = 0;
for (int i = 0; i < len; i++) {
int c = str[i] - 'a';
if (!trie[pos][c])
trie[pos][c] = ++tot;
pos = trie[pos][c];
}
vis[pos]++;
}
// DFS獲取Fail數組
void build()
{
queue<int> q;
// 根結點下的元素入隊
for (int i = 0; i < chSize; i++) {
if (trie[0][i]) {
fail[trie[0][i]] = 0;
q.push(trie[0][i]);
}
}
// DFS
while (!q.empty()) {
// 獲取隊首元素
int pos = q.front();
// 輸出隊首元素
// cout << "front: " << pos << endl;
// 隊首出隊
q.pop();
// 遍歷隊首元素的子節點
for (int i = 0; i < chSize; i++) {
// 若元素ch存在
if (trie[pos][i]) {
// 將fail數組的值置爲fail[隊首元素]的下一個ch對應的值
fail[trie[pos][i]] = trie[fail[pos]][i];
// 輸出構建fail數組的過程
// cout << "True char: " << (char)('a' + i) << " fail: " << trie[fail[pos]][i] << endl;
// 元素入隊
q.push(trie[pos][i]);
} else {
// 若不存在,將trie的值置爲fail[隊首元素]的下一個ch對應的值
// 方便下次計算
trie[pos][i] = trie[fail[pos]][i];
}
}
}
}
// 查詢
int query(char* str)
{
int len = strlen(str);
int pos = 0, ans = 0;
for (int i = 0; i < len; i++) {
int c = str[i] - 'a';
pos = trie[pos][c];
for (int j = pos; j && vis[j] != -1; j = fail[j]) {
ans += vis[j];
vis[j] = -1;
}
}
return ans;
}
};
AcAutomaton ac;
int main()
{
int t;
scanf("%d", &t);
while (t--) {
ac.init();
char str[maxM];
int n;
scanf("%d", &n);
for (int i = 0; i < n; i++) {
scanf("%s", str);
ac.insert(str);
}
ac.build();
scanf("%s", str);
printf("%d\n", ac.query(str));
}
}
注:代碼參考自哈爾濱理工大學ACM-ICPC集訓隊常用代碼庫
鏈接:https://pan.baidu.com/s/1eJwyrqCDQOAlPWC1mbPRKQ
密碼:gv5k
聯繫郵箱:[email protected]
CSDN:https://me.csdn.net/qq_41729780
知乎:https://zhuanlan.zhihu.com/c_1225417532351741952
公衆號:複雜網絡與機器學習
歡迎關注/轉載,有問題歡迎通過郵箱交流。