题目地址:
https://leetcode.com/problems/add-bold-tag-in-string/description/
给定一个长
n
n
n的字符串
s
s
s和一个单词列表
A
A
A,单词列表里的词都需要做加粗记号。求加上加粗记号的最终字符串。加粗记号指的是在
s
s
s的加粗的部分两边加上"<b>", "<b/>"
。注意,对于加粗记号,必须保证这个记号是包括了最长的一段,且不要嵌套,即加粗记号的紧邻的左右两边的字符必定不加粗。题目保证所有出现的字符只有英文字母和数字。
正解是AC自动机,参考https://blog.csdn.net/qq_46105170/article/details/125294891。先用 A A A构造一个AC自动机,然后对 s s s进行多模匹配。同时,开一个长 n n n的bool数组,标记每个位置是否要加粗。最后总结一下答案即可。代码如下:
class Solution {
public:
#define N 40000
int tr[N][62], ne[N], idx;
int len[N];
unordered_map<char, int> mp;
void insert(string s) {
int p = 0;
for (int i = 0; i < s.size(); i++) {
int c = mp[s[i]];
if (!tr[p][c]) tr[p][c] = ++idx;
p = tr[p][c];
}
// len[p]存的是p节点发生匹配的时候,要标记为加粗的字符个数
len[p] = s.size();
}
void build() {
queue<int> q;
for (int i = 0; i < 62; i++)
if (tr[0][i]) q.push(tr[0][i]);
while (q.size()) {
auto t = q.front(); q.pop();
for (int i = 0; i < 62; i++) {
int& p = tr[t][i];
if (p) ne[p] = tr[ne[t]][i], q.push(p);
else p = tr[ne[t]][i];
}
}
}
void query(string &s, vector<bool>& b, vector<string>& words) {
int p = 0;
for (int i = 0; i < s.size(); i++) {
p = tr[p][mp[s[i]]];
if (len[p])
// 加粗当前位置起前len[p]个字符
for (int j = 0; j < len[p]; j++)
if (!b[i - j]) b[i - j] = true;
else break;
}
}
string addBoldTag(string s, vector<string>& words) {
int cnt = 0;
for (char ch = 'A'; ch <= 'Z'; ch++) mp[ch] = cnt++;
for (char ch = 'a'; ch <= 'z'; ch++) mp[ch] = cnt++;
for (char ch = '0'; ch <= '9'; ch++) mp[ch] = cnt++;
for (int i = 0; i < words.size(); i++) insert(words[i]);
build();
vector<bool> b(s.size());
query(s, b, words);
string res;
for (int i = 0; i < s.size(); i++)
if (!b[i]) res += s[i];
else {
res += "<b>";
int j = i;
while (j < s.size() && b[j]) res += s[j++];
i = j - 1;
res += "</b>";
}
return res;
}
};
时间复杂度 O ( n + ∑ i A i ) O(n+\sum_i A_i) O(n+∑iAi),空间 O ( ∑ i A i ) O(\sum_i A_i) O(∑iAi)。